I've got a request that returns a list of responses in two possible structures, depending on the 'status'.
{
"listSize": 2,
"itemList": [
{
"id": ,
"Name": "",
"submittedOn": "",
"Reference": null,
"status": "Receipted",
"response": null
},
{
"id": 12345,
"submittedOn": "",
"Reference": null,
"status": "Failed",
"response": {
"xml": "",
"formErrors": [
{
"error_type": "",
"error_location":"",
"error_message": "",
}
]
}
},
]
}
I need to check the structure for the status being either 'Receipted' or 'Failed'. In Java I would use a for loop and an if statement within it to check the response field with different criteria depending on the 'status' field. (Example below)
for (int i = 0; i < response.length; i++){
if (response[i].status.equals("receipted")){
//do something
}
else{ //failed
//do something else
}
}
How could I achieve something similar in Karate? Should I use a Java Helper?
First, you are encouraged to write static expected results in tests. That said there are multiple ways to do this, here's one:
* def failedSchema = { xml: '#string', formErrors: '#array' }
* def isValid = function(x){ if (x.status == 'Receipted') return x.response == null; return karate.match(x.response, failedSchema).pass }
* match each response.itemList == '#? isValid(_)'
Here's another example: https://stackoverflow.com/a/62567412/143475
There are other ways to loop in Karate, but not really designed for matching: https://github.com/intuit/karate#loops
Here's an extreme example involving JSON transformation to make it easier to match: https://stackoverflow.com/a/53120851/143475
Also refer: https://github.com/intuit/karate#conditional-logic
Related
There are multiple keys present in x.details[0].user so how to compare in such condition. It works fine where there is only one key while when there are more than one key it fails with error as there are multiple keys for user.
Please guide
* def array =
"""
{
"type": "1",
"array": 2,
"details": [
{
"path": "path",
"user": {
"school": {
"name": [
{
"value": "this is school",
"codeable": {
"details": [
{
"hello": "yty",
"condition": "check1"
}
],
"text": "123S"
}
}
]
},
"sample": "test1",
"id": "22222"
},
"data": {
"check": "abc"
}
},
{
"path": "path",
"user": {
"school": {
"name": [
{
"value": "this is school",
"codeable": {
"details": [
{
"hello": "def",
"condition": "check2"
}
],
"text": "123O"
}
}
]
},
"sample": "test",
"id": "11111"
},
"data": {
"check": "xyz"
}
}
]
}
"""
* def lookup = { 'check1': 'yty', 'check2': 'def' }
* match each array.details contains { user: { school: { name[0]: { codeable: { details[0]: { hello: '#(lookup[_$.user.school.name[0].codeable.details[0].condition])' } } } } } }
I have tried multiple ways but not sure how to make it work when there are more than one keys getting returned.
First let me say this. Please, please do not try to be "too clever" in your tests. Read this please: https://stackoverflow.com/a/54126724/143475
Just have simple tests that test for specific, predictable responses and stop there. The maximum complexity should be match contains, that's it. What you are trying to do is in my opinion a waste of time, and will just cause grief for anyone who tries to maintain these tests in future.
That said, I'm giving you a completely different approach here. There are a few different ways, this is just one. Here we attack the problem piece by piece instead of trying to write one massive match.
* def allXyz = array.details.filter(x => x.data.check == 'xyz')
* match each allXyz..details == [{ hello: 'def', condition: 'check2' }]
You should be able to extend this to do all the weird assertions that you want. If you are wondering what allXyz..details does, you can print it like this:
* def temp = $allXyz..details
* print temp
I have the following JSON response (reference name: "list") and
[
{
"key": "101",
"val": {
"portCall": {
"id": 12664978
},
"status": "in-port"
}
},
{
"key": "102",
"val": {
"portCall": {
"id": 12415798
},
"status": "in-port"
}
},
{
"key": "103",
"val": {
"status": "on-voyage",
"voyage": {
"id": "7kynv-7lq85"
}
}
},
{
"key": "104",
"val": {
"status": "on-voyage",
"voyage": {
"id": "7kynv-2385"
}
}
}
]
also, I have an array list of few key values, evImos = [101,102,104]
In that, I have to identify the first key in the "list" response that has status as "on-voyage". So, the result should be "104".
I have tried the following and I need some help to make it work. Any help would be appreciated.
* def function getFirst = function(evImos) { for (let num of evImos) { let results = list.filter(d => d["key"] === num && d["val"]["status"] === "on-voyage"); if(results.length === 1) { karate.log(num); return num; } } }
* list.forEach(getFirst(evImos))
I'll just give you one hint. This one line will convert the whole thing in to a form that is much easier for you to validate:
* def temp = {}
* list.forEach(x => temp[x.key] = x.val.status)
Which gives you:
{
"101": "in-port",
"102": "in-port",
"103": "on-voyage",
"104": "on-voyage"
}
Now you can do:
* def isOnVoyage = function(key){ return temp[key] == 'on-voyage' }
Also read this: https://stackoverflow.com/a/59162760/143475
Thanks, to #Peter.
Based on his hint, I just tweaked it a little bit to match my requirement and it worked for me.
Here is the working copy for anyone to refer in the future.
* def temp = {}
* list.forEach(x => temp[x.key] = x.val.status)
* def isOnVoyage = function(keys){ for (let key of keys) { if(temp[key] == 'on-voyage'){ karate.log(key); karate.set('num', key); break; }}}
* isOnVoyage(evImos)
I have the following case and need to use firstKeyStr as a key, my code now is seeing "#firstKeyStr" as the key not the one stored in it
Code
Given path '/api/v1/sites'
When method get
Then status 200
And match response.success == true
And match response.data == "#object"
* def keys = karate.keysOf(response.data)
* def firstKeyStr = keys[0]
And match response.data."#firstKeyStr" == "#object"
Json Response Body
{
"success": true,
"data": {
"5ef34d0ca5a3c56ae14d2a23": {
"devices": [
"5f03192010a47f3e5b714193"
],
"groups": [
"5f0d9f30ef89e22778a2d230"
],
"users": [],
"triggers": [],
"alerts": [
"5f0d92b967bac60b84d3989b"
],
"name": "test",
"country": "US",
]
}
}
I'm looking for a way to pass this dynamic key (5ef34d0ca5a3c56ae14d2a23) in this line (And match response.data."#firstKeyStr" == "#object")
Since you are new to stack overflow, please read the following to best get help:
https://stackoverflow.com/help/how-to-ask
https://stackoverflow.com/help/someone-answers
As Peter said, your JSON is not well formed and the order of keys is not guaranteed (unless you have just a single key). The following code should get you going.
Sample Code:
Feature: firstKey
Scenario: firstKey
* def resp =
"""
{
"success": true,
"data": {
"5ef34d0ca5a3c56ae14d2a23": {
"devices": [
"5f03192010a47f3e5b714193"
],
"groups": [
"5f0d9f30ef89e22778a2d230"
],
"users": [],
"triggers": [],
"alerts": [
"5f0d92b967bac60b84d3989b"
],
"name": "test",
"country": "US"
}
}
}
"""
And match resp.data == "#object"
* def keys = karate.keysOf(resp.data)
* def firstKeyStr = keys[0]
* print firstKeyStr
* print (resp.data[firstKeyStr])
And match (resp.data[firstKeyStr]) == "#object"
Use round brackets so that JS is used instead of JsonPath:
* def response = { data: { a: 1, b: 2 } }
* def keys = karate.keysOf(response.data)
* def first = keys[0]
* match (response.data[first]) == 1
Here is the sample code
"xNotification": [
{
"code": "1234",
"text": "Invalid Data"
"yNotification": [
{
"code": "345",
"text": "Invalid Data"
}
]
}
]
}
yNotification array only exits sometimes in the response, when it exists I need to capture the code value and put it in a variable. I don't want assert just when it exists I need to put it in a variable. I am newbie to any testing frameworks. I appreciate any help with this issue. Thanks in advance.
Please read this part of the documentation: https://github.com/intuit/karate#conditional-logic
Example:
* def expected = zone == 'zone1' ? { foo: '#string' } : { bar: '#number' }
* match response == expected
EDIT: This is the complete solution to your question:
* def response =
"""
{ "xNotification": [
{
"code": "1234",
"text": "Invalid Data",
"yNotification": [
{
"code": "345",
"text": "Invalid Data"
}
]
}
]
}
"""
* def code = karate.get('$.xNotification[0].yNotification[0].code')
* print code
In the below JSON response, I need to extract the 'cid' for the record that has the 'nationalityDecription' as 'USA'. By using this query as a reference, I used the below loc in the karate feature file, but 1st line itself fails with syntax error(tried different combinations). For now, I'm using the custom javascript as a workaround which is working fine. I need help to check if i'm missing anything in syntax. Thanks
Response:
{
"header": {
"Id": "12345678",
"timeStamp": "2018-09-17T10:09:812.000"
},
"dataRecords": [
{
"cid": "31H678",
"cidMeta": "00",
"nationalityDecription": "CHINA"
},
{
"cid": "31S421",
"cidMeta": "01",
"nationalityDecription": "USA"
}
]
}
Feature file:
* def record= $response.dataRecords[?(#.nationalityDecription=='USA')]
* def cid = record.cid
* def response = { "header": { "Id": "12345678", "timeStamp": "2018-09-17T10:09:812.000" }, "dataRecords": [ { "cid": "31H678", "cidMeta": "00", "nationalityDecription": "CHINA" }, { "cid": "31S421", "cidMeta": "01", "nationalityDecription": "USA" } ] }
* def cid = get[0] response.dataRecords[?(#.nationalityDecription=='USA')].cid
* match cid == '31S421'