Using a JSON file in another JSON file (for code reusabibility)? It's possible? (Karate/APITesting/SchemaValidation) - api

I'm working with a lot of API data and my plan is to do schema validation using Karate. Because I have many items which share some properties I would like to create JSON files and "call" them in the principal JSON file where I have the whole schema.
I understand I could call each json in the feature file, but I would like to know if there is any way I can put all the schemas together, like a puzzle, from multiple json files in a single json file and call just one in the feature file.
Thanks! P.S Please save my ass!

Take a look at this example: https://github.com/ptrthomas/karate-test/tree/main/src/test/java/examples/reuse
So you can "compose" multiple JSON files in a re-usable feature file like this:
#ignore
Feature:
Scenario:
* def first = read('first.json')
* def second = read('second.json')
* def schema = { first: '#(first)', second: '#[] second' }
And then when you want to use this to match, note how the call is done in "shared" scope to keep things simple:
* call read('common.feature')
* def response = { first: { a: 1 }, second: [{ b: 'x' }] }
* match response == schema

Related

Is it possible to generate scenario outline definition dynamically [duplicate]

I currently use junit5, wiremock and restassured for my integration tests. Karate looks very promising, yet I am struggling with the setup of data-driven tests a bit as I need to prepare a nested data structures which, in the current setup, looks like the following:
abstract class StationRequests(val stations: Collection<String>): ArgumentsProvider {
override fun provideArguments(context: ExtensionContext): java.util.stream.Stream<out Arguments>{
val now = LocalDateTime.now()
val samples = mutableListOf<Arguments>()
stations.forEach { station ->
Subscription.values().forEach { subscription ->
listOf(
*Device.values(),
null
).forEach { device ->
Stream.Protocol.values().forEach { protocol ->
listOf(
null,
now.minusMinutes(5),
now.minusHours(2),
now.minusDays(1)
).forEach { startTime ->
samples.add(
Arguments.of(
subscription, device, station, protocol, startTime
)
)
}
}
}
}
}
return java.util.stream.Stream.of(*samples.toTypedArray())
}
}
Is there any preferred way how to setup such nested data structures with karate? I initially thought about defining 5 different arrays with sample values for subscription, device, station, protocol and startTime and to combine and merge them into a single array which would be used in the Examples: section.
I did not succeed so far though and I am wondering if there is a better way to prepare such nested data driven tests?
I don't recommend nesting unless absolutely necessary. You may be able to "flatten" your permutations into a single table, something like this: https://github.com/intuit/karate/issues/661#issue-402624580
That said, look out for the alternate option to Examples: which just might work for your case: https://github.com/intuit/karate#data-driven-features
EDIT: In version 1.3.0, a new #setup life cycle was introduced that changes the example below a bit.
Here's a simple example:
Feature:
Scenario:
* def data = [{ rows: [{a: 1},{a: 2}] }, { rows: [{a: 3},{a: 4}] }]
* call read('called.feature#one') data
and this is: called.feature:
#ignore
Feature:
#one
Scenario:
* print 'one:', __loop
* call read('called.feature#two') rows
#two
Scenario:
* print 'two:', __loop
* print 'value of a:', a
This is how it looks like in the new HTML report (which is in 0.9.6.RC2 and may need more fine tuning) and it shows off how Karate can support "nesting" even in the report, which Cucumber cannot do. Maybe you can provide feedback and let us know if it is ready for release :)

How to restrict execution of a feature file to once while using call with array parameter ( execute __loop 0 )

Here is my situation:
I have a sequence of steps to create users based on a random input i.e between 1 - 5.
To do this , I have a js function that loops thru based on random input and call is made to a re-usable feature to create users. User-id is stored in a json array after every create user request as I need to active these users later.
This is the output I get from my user creation steps
def userIDs = [{id: user1}, {id: user2}, {id: user3}]
User activation steps:
Here I am calling a reusable feature with above generated array with userIDs parameter.
def result = call read('r_userActivation.feature') userIDs
I have a js function in r_userActivation feature to generate dynamic payload for user activation request based on no. of users created. Payload is something like this
<users>
<user>user1</user>
<user>user2</user>
<user>user3</user>
</users>
As my parameter is array, feature file that I am calling is trying to execute in loop to the extent of my array length.
Is there a way to restrict call statement execution __loop to once as I don't need my request to loop.
Any help is much appreciated. Thanks
Just re-shape the data:
* def users = { ids: '#(userIDs)' }
Also look at JSON transforms: https://github.com/intuit/karate#json-transforms

How to prepare a nested data structure for a data-driven test in Karate?

I currently use junit5, wiremock and restassured for my integration tests. Karate looks very promising, yet I am struggling with the setup of data-driven tests a bit as I need to prepare a nested data structures which, in the current setup, looks like the following:
abstract class StationRequests(val stations: Collection<String>): ArgumentsProvider {
override fun provideArguments(context: ExtensionContext): java.util.stream.Stream<out Arguments>{
val now = LocalDateTime.now()
val samples = mutableListOf<Arguments>()
stations.forEach { station ->
Subscription.values().forEach { subscription ->
listOf(
*Device.values(),
null
).forEach { device ->
Stream.Protocol.values().forEach { protocol ->
listOf(
null,
now.minusMinutes(5),
now.minusHours(2),
now.minusDays(1)
).forEach { startTime ->
samples.add(
Arguments.of(
subscription, device, station, protocol, startTime
)
)
}
}
}
}
}
return java.util.stream.Stream.of(*samples.toTypedArray())
}
}
Is there any preferred way how to setup such nested data structures with karate? I initially thought about defining 5 different arrays with sample values for subscription, device, station, protocol and startTime and to combine and merge them into a single array which would be used in the Examples: section.
I did not succeed so far though and I am wondering if there is a better way to prepare such nested data driven tests?
I don't recommend nesting unless absolutely necessary. You may be able to "flatten" your permutations into a single table, something like this: https://github.com/intuit/karate/issues/661#issue-402624580
That said, look out for the alternate option to Examples: which just might work for your case: https://github.com/intuit/karate#data-driven-features
EDIT: In version 1.3.0, a new #setup life cycle was introduced that changes the example below a bit.
Here's a simple example:
Feature:
Scenario:
* def data = [{ rows: [{a: 1},{a: 2}] }, { rows: [{a: 3},{a: 4}] }]
* call read('called.feature#one') data
and this is: called.feature:
#ignore
Feature:
#one
Scenario:
* print 'one:', __loop
* call read('called.feature#two') rows
#two
Scenario:
* print 'two:', __loop
* print 'value of a:', a
This is how it looks like in the new HTML report (which is in 0.9.6.RC2 and may need more fine tuning) and it shows off how Karate can support "nesting" even in the report, which Cucumber cannot do. Maybe you can provide feedback and let us know if it is ready for release :)

Reuse feature/scenario response

I have an architectural problem, as I am wondering how to structure my features. I will give an example what I want to achieve, without code duplication.
-scenarios
--directoryA
---feature1
----scenario1
----scenario2
--directoryB
---feature2
----scenario3
Feature1: Users
#create_user
Scenario1: Create a User
* url 'someUrl'
Given request `somerequest`
When method post
Then status 201
And match response == { id: #uuid }
* def userId = response.id
#create_user_key
Scenario2: Create a User key
* url 'someUrl'
* def createUser = call read('this:users.feature#create_user')
Given path 'userId', 'keys'
When method post
Then status 201
And match response == { key: #string }
* def userKey = response.key
Feature2: Tokens
Scenario3: Create token
* url 'someUrl'
* def createUser = call read('classpath:scenarios/directoryA/feature1.feature#create_user_key')
* def userHeader =
"""
function() {
return "Bearer " + userKey
}
"""
Given path 'userId', 'tokens'
And header Authorization = userHeader
When method post
Then status 201
As far as I know, it is suggested Scenario1 and Scenario2 to be in a separate file. Here comes my question:
I need both Scenario1 and Scenario2 in order Scenario3 to be executed (userId and userKey are needed). If I call Scenario2, where should I store values in order to avoid code duplication?
I am aware that Scenario does not store values, but I do not want to create Background with Scenario1 and Scenario2 in Feature2, when these are stored in another feature. It doesn't matter whether it's a one scenario per feature or more.
Thanks
Take some time and read this section of the documentation very carefully: https://github.com/intuit/karate#calling-other-feature-files - and then read the section on "Shared Scope".
Short answer - you can "return" variables from a called feature. I don't see the problem, after the call - you can start using variables from createUser like this (or refer to it directly by path):
* def userId = createUser.userId
All this said - I will caution you that you appear to have gotten into the trap of attempting "too much reuse" - a common problem with teams doing test-automation. Yes, sometimes "reuse is bad" - it is okay if you don't believe me - but this needs to be said. For an example, please read this answer: https://stackoverflow.com/a/54126724/143475

How to use a JSON response data set from a feature to build multiple new endpoints

I have a json file with different sets of data (array of more than 5 rows). I would like to use it as path and query parameters to build new endpoints and validate the response of newly built endpoints to find a specific value. I would like to run it as loop.
And I would like you to actually take some time to read the documentation and then ask specific questions. Please refer this:
https://github.com/intuit/karate#dynamic-scenario-outline
Feature: scenario outline using a dynamic table
Background:
* def kittens = read('../callarray/kittens.json')
Scenario Outline: cat name: <name>
Given url demoBaseUrl
And path 'cats'
And request { name: '<name>' }
When method post
Then status 200
And match response == { id: '#number', name: '<name>' }
# the single cell can be any valid karate expression
# and even reference a variable defined in the Background
Examples:
| kittens |
Thanks Peter. I am trying to test whether an event is published into kafka topic. It is 2 step validation
Step 1: Find out the start & end offset and partitions present in a topic ( a JSON response)
Step 2: Use the data from JSON response from Step 1 to dig through each partition to find the right event.
Checking if Karate can support this.