I am making a SOAP request, and I am receiving the response that's returned as an array:
- [print] [
"M4205N",
"M4206U"
]
For each item in the array, I want to make another SOAP request. I've read how you can do this with tables and call a feature file, and I've read how to loop through an array, and call a js function. I cannot figure out how to loop through the array, and pass each value to another SOAP request XML (one at a time).
I want to do something like this:
Given soapURL
And method post
def responseArray = /xml path for the codes I want/
def result = call read('otherRequest.feature') responseArray
The otherRequest.feature file would look something like this:
#ignore
Feature:
Background:
* def myNewRequest = read('soap.xml')
Scenario:
Given soapURL
* replace myNewRequest
| token | value |
| ##refNum## | responseArrayValue |
When request myNewRequest
And method post
However, I get this error:
GetNewMessageList.feature:27 - argument not json or map for feature call loop array position: 0, M4205N
How can I loop through each item in the array, and pass each value to the other feature file?
The addition of this one line should do what you want. Yes there is a hard requirement that the "loop" array should be an array of JSON objects. But you can convert an array of primitives in one step:
* def responseArray = karate.mapWithKey(responseArray, 'responseArrayValue')
This is documented here: https://github.com/intuit/karate#json-transforms
Related
I am trying to pass a JSON Array Data Source to form a dynamic path for my API tests.
I am converting an existing Array (which I get back from another APIand JS function) to a JSON Array using karate.mapWithKey in the same feature file.
When I print the value of input, I do see a properly formatted list of objects as below
[
{
"keyUUID": "1234"
},
{
"keyUUID": "5678"
}
]
However, when I run the whole feature, I am seeing an error as org.graalvm.polyglot.PolyglotException: ReferenceError: "input" is not defined
Feature: Data-Driven Feature
Scenario Outline: looping over list of json objects
* def keys = ['1234','5678']
* def input = karate.mapWithKey(keys,'keyUUID')
* print input
* When path is <name>
Examples:
| input |
Am I missing something here?
I tried reading through a json file , which works fine but the json array of objects created using mapWithKey always gives an error.
I was able to resolve it after moving the input [json array of objects creation] from within scenario outline to Background
Feature: Data-Driven Feature
Background:
* def keys = ['1234','5678']
* def input = karate.mapWithKey(keys,'keyUUID')
* print input
Scenario Outline: looping over list of json objects
* When path is <name>
Examples:
| input |
The reference error I was receiving earlier for input is resolved now.
This question already has an answer here:
How to set dynamic value as a key to json string in request
(1 answer)
Closed 1 year ago.
I am making a REST API call which returns a response like this
{"id":"726295ab-d6bc-4f09-8cb7-6f6f54fc9364", "name":"Customer Data"}
I create 5 objects like this and I want to store the ids of all the 5 objects from response in 5 different variables.
I tried using something like
* def catID_<categoryName> = $.id
and provided the name of the object in the Examples section. It works fine most of the times except when the name has spaces in it.
no step-definition method match found for: * def catID_Customer Data = $.id
Is it possible to do something like this?
* def catName = replace all spaces in the name with _
* def #(catName)_id = $.id
or is there a better way to achieve this?
You seem to be doing things Karate is not designed to do, so by default please assume that this is not supported.
Most likely, adding keys to a JSON object is a more elegant approach instead of trying to dynamically hack def. For example:
* def variables = {}
* variables['<someDynamicName>'] = $.id
# then later
* print variables['actual name']
Also note that the '< and >' are not required: https://github.com/karatelabs/karate#scenario-outline-enhancements
We're using Karate for backend testing of a microservice. I'd like to be able to make N calls to the backend API, where N is configurable as a number without having to do ugly things.
This was my first approach:
Given url baseUrl
And headers HEADERS
When method get
Then status 200
Given url baseUrl
And headers HEADERS
When method get
Then status 200
Given url baseUrl
And headers HEADERS
When method get
Then status 200
(Just repeating the call) It works, but obviously does not scale well (imagine 1000 of these).
Next approach was a bit better - I put the call in a separate feature and used the https://github.com/intuit/karate#data-driven-features approach:
* table jwts
| headers |
| HEADERS |
| HEADERS |
| HEADERS |
| HEADERS |
| HEADERS |
* def result = call read('call-once.feature') jwts
Slightly better but still does not scale. We also tried varieties of karate.repeat() which seems like the most natural approach, but had trouble with the syntax. None of the examples I could find had an API call inside of a for-each.
* def callFunction = function (HEADERS) { read('call-putaway-once.feature'); { HEADERS: '#(HEADERS)'} }
* def result = karate.repeat(5, callFunction)
But couldn't get any varieties of that working.
Can anyone provide an example of how to repeat the same exact Karate lines N times? I'm really looking for something like:
for (int i = 0; i < numTimes; i++) {
Given url baseUrl
And headers HEADERS
When method get
Then status 200
}
(Or functionally equivalent).
Thanks!
Here you go. First, the second called.feature:
#ignore
Feature:
Scenario:
Given url 'http://httpbin.org'
And path 'headers'
And header X-Karate = count
When method get
Then status 200
And now you can do this in your first feature:
* def fun = function(x){ return { count: x } }
* def data = karate.repeat(5, fun)
* call read('called.feature') data
P.S. by the way search the readme for "polling", there is an example of an API call in a loop: polling.feature
Karate almost have a feature to do this : retry until.
This feature doesn't repeat "n" time, but repeat until a condition is not validate
Example here : polling.feature
For a simple request it's seems like :
Given url baseUrl
And headers HEADERS
And retry until responseStatus == 200
When method get
I have a very large response array I want to assert against, but without knowing the order. I have a variable with the expected response values so I can do a single giant comparison, but I'm unable to load the entire response and compare it with the entire expected response variable at the same time.
* def obligationsQuery = Java.type("tests.account.sql.Obligations").getObligations(division, account)
* def getObligations = db.readRows(obligationsQuery)
Given path "account", "v1", "accounts", systemId, "obligations"
And header api-key = gatewayKey
When method GET
Then status 200
And match $.data != null
And match $.data[*].transactionType contains any "<transactionTypeResponse>"
And match $.data[*] contains only getObligations
Examples:
| description | transactionType | transactionTypeResponse |
| Invoice | 001 | invoice
The error I get is:
get_obligations_collection.feature:49 - path: $.data[*][*], actual: [{"object1"}, {"object2"}, {"etc"}], expected: {"object1"}, reason: actual value does not contain expected
I've also tried:
And match each $.data[*] contains only getObligations
But then I get:
get_obligations_collection.feature:49 - path: $[0], actual: [{"object1"}, expected: [{"object1"}, {"object2"}, {"etc"}, reason: actual value is not list-like
I assume $.data is a JSON array so no need to use json-path to again get the data into another array by calling as $.data[*].
so,
And match $.data contains only getObligations
should work.
If this still not working, please provide some proper response and getObligations values to investigate further.
I assumed that contains only would show the complete value of my variable, but smartly, it only shows the object that fails to match the api response object. Was able to verify that through a simpler assert, and then checking the error message saw that an ID was missing padding that the API adds, but the DB does not.
As usual, just looking more closely at the data returned provides a simple explanation.
I need to pass multiple query parameters with the same name in a URL, but I am having problems getting it to work with Karate. In my case, the URL should look like this:
http://mytestapi.com/v1/orders?sort=order.orderNumber&sort=order.customer.name,DESC
Notice 2 query parameters named "sort". I attempted to create these query string parameters with Karate, but only the last "sort" parameter gets created in the query string. Here are the ways I tried to do this:
Given path 'v1/orders'
And param sort = 'order.orderNumber'
And param sort = 'order.customer.name,DESC'
And header Authorization = authInfo.token
And method get
Then status 200
And:
Given path 'v1/orders'
And params sort = { sort: 'order.orderNumber', sort: 'order.customer.name,DESC' }
And header Authorization = authInfo.token
And method get
Then status 200
And:
Given path 'v1/order?sort=order.orderNumber&sort=order.customer.name,DESC'
And header Authorization = authInfo.token
And method get
Then status 200
The first two ways provide the same query string result: ?sort=order.customer.name%2CDESC
The last example does not work because the ? get encoded, which was expected and explained in this post - Karate API Tests - Escaping '?' in the url in a feature file
It's clear that the second "sort" param is overriding the first and only one parameter is being added to the URL. I have gone through the Karate documentation, which is very good, but I have not found a way to add multiple parameters with the same name.
So, is there a way in Karate to set multiple URL query parameters with the same name?
Yes you can generate multiple query parameters with the same name in karate
All values of similar key should be provided in an array.
Given path 'v1/orders'
And params {"sort":["order.orderNumber","order.customer.name,DESC"]}
And header Authorization = authInfo.token
And method get
Then status 200
And for setting single parameter using param it will be like
And param sort = ["order.orderNumber","order.customer.name,DESC"]