How to loop new request according to response data list from previous step [duplicate] - karate

I am using Karate version 0.8.0.1 and I want to perform following steps to test some responses.
I make a Get to web service 1
find the value for currencies from the response of web service 1 using jsonpath: $.currencies
Step 2 gives me following result: ["USD","HKD","SGD","INR","GBP"]
Now I use Get method for web service 2
From the response of web service 2 I want to get the value of price field with json-path something like below(passing the values from step 3 above):
$.holding[?(#.currency=='USD')].price
$.holding[?(#.currency=='HKD')].price
$.holding[?(#.currency=='SGD')].price
$.holding[?(#.currency=='INR')].price
$.holding[?(#.currency=='GBP')].price
So there are so many currencies but I want to verify price for only the currencies returned by web service 1(which will be always random) and pass it on to the the output of web service 2 to get the price.
Once i get the price I will match each price value with the value returned from DB.
I am not sure if there is any simple way in which I can pass the values returned by service 1 into the json-path of service 2 one by one and get the results required. Any suggestions for doing this will be helpful As this will be the case for most of the web services I will be automating.

There are multiple ways to do this in Karate. The below should give you a few pointers. Note how there is a magic variable _$ when you use match each. And since you can reference any other JSON in scope, you have some very powerful options.
* def expected = { HKD: 1, INR: 2, USD: 3}
* def response1 = ['USD', 'HKD', 'INR']
* def response2 = [{ currency: 'INR', price: 2 }, { currency: 'USD', price: 3 }, { currency: 'HKD', price: 1 }]
* match response2[*].currency contains only response1
* match each response2 contains { price: '#(expected[_$.currency])' }
You probably already have seen how you can call a second feature file in a loop which may be needed for your particular use case. One more piece of the puzzle may be this - it is very easy to transform any JSON array into the form Karate expects for calling a feature file in a loop:
* def response = ['USD', 'HKD', 'INR']
* def data = karate.map(response, function(x){ return { code: x } })
* match data == [{code: 'USD'}, {code: 'HKD'}, {code: 'INR'}]
EDIT - there is a short-cut to convert an array of primitives to an array of objects now: https://stackoverflow.com/a/58985917/143475
Also see this answer: https://stackoverflow.com/a/52845718/143475

Related

How to iterate over a dynamic array of objects and use each object as a parameter in test?

I started my adventure with Karate a month ago. I have a simple GET test called getAllCars.feature showing a list of cars currently available:
[
{
"brandName": "BMW",
"id": 1,
"winterTires": false,
"modelName": "X5"
},
{
"brandName": "Opel",
"id": 34,
"winterTires": true,
"modelName": "Insignia"
},
{
"brandName": "Mercedes-Benz",
"id": 36,
"winterTires": true,
"modelName": "GLE Coupe"
},
{
"brandName": "Huydai",
"id": 251,
"winterTires": false,
"modelName": "i30"
}
]
I have to use each id as a parameter for the next feature file, the problem is, the list of cars is dynamic, ids don't repeat and I will have to use this list of ids for several other feature files. I managed to create a helper getCarIds.feature, which creates an array of objects "carId": "#number":
Feature: Get car IDs
Scenario: Get car IDs
* call read('classpath:x/automation/cars/getAllCars.feature')
* def carIds = $response[*].id
* def carFeeder = karate.mapWithKey(carIds, 'carId')
The following getCarParameters.feature has to iterate over the array from getCarIds.feature and pass each id as a parameter to get a response with performance parameters of each car and I don't know how to use each id separately as a parameter (keeping in mind that the list of ids is changing):
Feature: Get parameters of each car
Scenario: Get parameters for each car
* call read('classpath:x/automation/cars/getCarIds.feature')
Given url carUrl + '/carparameters'
And param carId =
When method GET
Then status 200
I managed to do it when passing the values from getCarIds.feature to getCarParameters.feature like described here by adding following line to getCarIds.feature:
* call read('classpath:x/automation/cars/getCarParameters.feature') carFeeder
but several other tests require car ids. I need getCarIds.feature to be reusable, so I would have to retrieve data from feature file, which creates the array with ids, instead of passing it to the GET feature and apparently it isn't so easy. Maybe my approach is completely wrong.
I think this is a valid case for karate.callSingle(): https://github.com/karatelabs/karate#karatecallsingle
So you can actually stick this in any feature and it is guaranteed to execute only once across your test suite. If the data is indeed something used by a majority of your test suite, you could even do this initialization in karate-config.js.
So this should work. First the reusable feature common.feature. Instead of the hard-coded response, you know how to make an actual HTTP request.
#ignore
Feature:
Scenario:
* def response =
"""
[
{
"brandName": "BMW",
"id": 1,
"winterTires": false,
"modelName": "X5"
},
{
"brandName": "Opel",
"id": 34,
"winterTires": true,
"modelName": "Insignia"
}
]
"""
* print 'getting car ids'
* def carIds = response.map(x => ({ id: x.id }))
Note the use of the JS map() function above, which I have started to recommend instead of JsonPath.
And here is a feature that uses the above. This uses the new #setup annotation that makes it easy to "loop": https://github.com/karatelabs/karate#setup
You can try this example quickly, and watch it make 2 requests using a param id from the loop.
Feature:
#setup
Scenario:
* def data = karate.callSingle('call-single-common.feature').carIds
Scenario Outline:
* url 'https://httpbin.org/get'
* param id = id
* method get
Examples:
| karate.setup().data |
There are other ways to loop, refer: https://github.com/karatelabs/karate#data-driven-features

Karate - how to use param from response to test child endpoints

I have a parent URL that returns an array of objects with id and slug, this is dynamically generated because it depends of the user (test env). After getting this data, how can I dynamically call each element object from an array and make another request ?
Example :
Scenario: parent
Given url 'posts'
Then Status 200
* assert response.status == true
Example url/posts returns a data of array of objects :
[
{id: 6, slug: 'my-post-6'},
{id: 23, slug: 'example-test-23'},
{id: 133, slug: 'another-test-133'},
]
Then I want to make a get request to every object like so : url 'posts/' data.slug
How can I do this ?
GET url 'posts/my-post-6'
// Validate schema
GET url 'posts/example-test-23'
// Validate schema
GET url 'posts/another-test-133'
// Validate schema
Karate's call keyword auto-loops over a JSON array of objects. Refer the docs: https://github.com/karatelabs/karate#data-driven-features
You can try this simple example:
Feature:
Scenario:
* def data = [{ slug: 'one'}, { slug: 'two'}]
* call read('#called') data
#called #ignore
Scenario:
* url 'https://httpbin.org/anything'
* path slug
* method get
This is getting improved in future versions, refer: https://github.com/karatelabs/karate/issues/1905 (available now in 1.3.0.RC2)

Can anyone explain how to get all the mismatch between two Array of JSON Object responses in karate? [duplicate]

Like if I have two JSON as below and I want to check the mismatch between those
JSON 1:
{
name:'john',
contact:'123',
country:'america'
}
JSON 2:
{
name:'vishal',
contact:'123',
country:'India'
}
Now it will return me with the mismatch between name and country not only the name?
No this is not supported. We feel this is not needed, because in your regular CI runs you only care if the test passed or failed, and you see the details in the log.
Also note that you can simulate this if you really want using a Scenario Outline: https://stackoverflow.com/a/54108755/143475
Finally, if you care so much about this, kindly contribute code, this is open-source after all.
EDIT: you can easily do this by iterating over keys. Here is the code:
EDIT2: Setting up data via a Background is no longer supported in version 1.3.0 onwards, please look at the #setup tag: https://github.com/karatelabs/karate#setup
Feature:
Background:
* def json1 = { name: 'john', contact: '123', country: 'america' }
* def json2 = { name: 'vishal', contact: '123', country: 'India' }
* def keys = karate.keysOf(json1)
* def data = karate.mapWithKey(keys, 'key')
Scenario Outline: <key>
* match (json1[key]) == json2[key]
Examples:
| data |
And here is the report:

Karate contains and all key-values did not match error

I try to learn Karate but have some issue and I can't resolve it by myself.
So my Feature is looking rather simple:
Feature: Alerting get the list of all alerts
Background:
* url 'url'
Scenario: Retrieve all alerts
Given path '5c348c553a892c000bb1f2dd'
When method get
Then status 200
And match response contains {id: 5c348c553a892c000bb1f2dd}
The case here is to fetch a response and make sure that given ID is on the list. As far I understand this documentation keyword contains should lookup only for the given phrase but I get an error: reason: all key-values did not match
This is my console output:
allAlertsGet.feature:10 - path: $, actual: {data={name=Baelish of Harrenhal, user=griffin, id=5c348c553a892c000bb1f2dd, tags=["Gared"], triggers={prometheus=[{"js_id":"Qarth","labels":["Harry Potter and the Sorcerer's Stone"],"operator":"==","query":"up","value":"1"}]}, trigger_interval=398s, receivers={slack=[{"holdoffTime":"0s","id":"Stalls","message":"Dark and difficult times lie ahead. Soon we must all face the choice between what is right and what is easy.","revokeMessage":"Every flight begins with a fall.","token":"Buckbeak"}]}, hold_cap=4, max_cap=16, factor=2, createDate=1546947669, triggered_date=1546948867, mute_until=0, muted=false, status=3}}, expected: {id=5c348c553a892c000bb1f2dd}, reason: all key-values did not match
What I have missed? I use karate 0.9.0.
Pay attention to the nested structure of your JSON. You can paste this snippet into a Scenario and try it, this is a tip - you can experiment quickly without making HTTP requests like this:
* def response = { data: { name: 'Baelish of Harrenhal', user: 'griffin', id: '5c348c553a892c000bb1f2dd' } }
* match response.data contains { id: '5c348c553a892c000bb1f2dd' }
EDIT: just to show off a few other ways to do assertions:
* match response.data.id == '5c348c553a892c000bb1f2dd'
* match response..id contains '5c348c553a892c000bb1f2dd'
* def id = { id: '5c348c553a892c000bb1f2dd' }
* match response == { data: '#(^id)' }
* match response contains { data: '#(^id)' }

How to loop through karate response array and pass this in json path of another web service response

I am using Karate version 0.8.0.1 and I want to perform following steps to test some responses.
I make a Get to web service 1
find the value for currencies from the response of web service 1 using jsonpath: $.currencies
Step 2 gives me following result: ["USD","HKD","SGD","INR","GBP"]
Now I use Get method for web service 2
From the response of web service 2 I want to get the value of price field with json-path something like below(passing the values from step 3 above):
$.holding[?(#.currency=='USD')].price
$.holding[?(#.currency=='HKD')].price
$.holding[?(#.currency=='SGD')].price
$.holding[?(#.currency=='INR')].price
$.holding[?(#.currency=='GBP')].price
So there are so many currencies but I want to verify price for only the currencies returned by web service 1(which will be always random) and pass it on to the the output of web service 2 to get the price.
Once i get the price I will match each price value with the value returned from DB.
I am not sure if there is any simple way in which I can pass the values returned by service 1 into the json-path of service 2 one by one and get the results required. Any suggestions for doing this will be helpful As this will be the case for most of the web services I will be automating.
There are multiple ways to do this in Karate. The below should give you a few pointers. Note how there is a magic variable _$ when you use match each. And since you can reference any other JSON in scope, you have some very powerful options.
* def expected = { HKD: 1, INR: 2, USD: 3}
* def response1 = ['USD', 'HKD', 'INR']
* def response2 = [{ currency: 'INR', price: 2 }, { currency: 'USD', price: 3 }, { currency: 'HKD', price: 1 }]
* match response2[*].currency contains only response1
* match each response2 contains { price: '#(expected[_$.currency])' }
You probably already have seen how you can call a second feature file in a loop which may be needed for your particular use case. One more piece of the puzzle may be this - it is very easy to transform any JSON array into the form Karate expects for calling a feature file in a loop:
* def response = ['USD', 'HKD', 'INR']
* def data = karate.map(response, function(x){ return { code: x } })
* match data == [{code: 'USD'}, {code: 'HKD'}, {code: 'INR'}]
EDIT - there is a short-cut to convert an array of primitives to an array of objects now: https://stackoverflow.com/a/58985917/143475
Also see this answer: https://stackoverflow.com/a/52845718/143475