How to compare 2 JSON objects containing array using Karate tool [duplicate] - api

This question already has an answer here:
Is there a simple match for objects containing array where the array content order doesn't matter?
(1 answer)
Closed 1 year ago.
One of the API testing using intuit/karate,
Expected JSON is: {name: hello,
config:[{username: abc, password: xyz},{username: qwe, password: tyu}]}
There is two possibility of an API response.
First possible actual JSON: {name: hello,
config:[{username: qwe, password: tyu},{username: abc, password: xyz}]}
Second possible actual JSON: {name: hello,
config:[{username: abc, password: xyz},{username: qwe, password: tyu}]}
Likewise, the sequence of array element is different in actual response, hence following approach of validation of response throws error randomly.
And response == < ExpectedResponse >
And response contains < ExpectedResponse >
Sometimes error is thrown as :
Error : { Actual: response.config[0].abc, Expected: response.config[0].qwe }
Sometimes error is thrown as :
Error : { Actual: response.config[0].qwe, Expected: response.config[0].abc }
Would you please provide exact karate approach of JSON validation in which entire JSON along with ignore the sequence of element in JSON containing array ?

Here is the solution:
* def response1 = {name: 'hello', config:[{username: 'qwe', password: 'tyu'},{username: 'abc', password: 'xyz'}]}
* def response2 = {name: 'hello', config:[{username: 'abc', password: 'xyz'},{username: 'qwe', password: 'tyu'}]}
* def config = [{username: 'qwe', password: 'tyu'},{username: 'abc', password: 'xyz'}]
* match response1 == { name: 'hello', config: '#(config)' }
* match response2 == { name: 'hello', config: '#(^^config)' }

Related

Null values in CSV Scenario Outline [duplicate]

I am able to read a csv file and convert it to json by
def expectedResponse = read('classpath:somefile.csv')
Suppose I have csv file as below
name,age
praveen,29
joseph,20
1.It is converting all elements as string and stores in the variable as json. How to keep the number as a number ? because it causes match failure which i do later with the actual response.
2.How to get the value 20. Like by specifying joseph, I want to get the age.
I got the jsonpath as
get expectedResponse $.[?(#.member == '<name>')].age
I get the name from examples. So I get it as joseph in runtime. But i get error as reason: not equal (Integer : JSONArray). It is not returning the age alone (Integer value)
Or is there any better way to get it ?
The CSV format does not contain any type information, so everything defaults to "string" and you have to convert it yourself. But this is easy using karate.map().
* text users =
"""
name,age
praveen,29
joseph,20
"""
* csv users = users
* match users == [{ name: 'praveen', age: '29' }, { name: 'joseph', age: '20' }]
* def fun = function(x){ x.age = ~~x.age; return x }
* def users = karate.map(users, fun)
* match users == [{ name: 'praveen', age: 29 }, { name: 'joseph', age: 20 }]

Trouble using #regex marker

I'm having trouble constructing a proper #regex marker.
I'm trying to match on a couple of date formats in my response JSON:
"created": "2017-03-23T14:16:25.854Z"
"modified": "2018-06-21T05:38:37.978Z"
I've attempted the following markers:
'#regex [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z'
'#regex [0-9]{4}-[0-9]{2}-[0-9]{2}.[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}.'
'#regex \d+-\d+-\d+.\d+:\d+:\d+.\d+.'
All 3 forms appear to be correct (according to rubular.com). I've also played around with escaping characters that might be problematic. The only one I been able to get tot work so far is:
[0-9-T:.Z]+
But this feels a little "loose" of a pattern match.
Basically I'm attempting this:
* def meta = { created: '#regex[[0-9]{4}-[0-9]{2}-[0-9]{2}.[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}.]', modified: '#regex[[0-9]{4}-[0-9]{2}-[0-9]{2}.[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}.]' }
And match each response ==
"""
{
id: '#regex [a-z0-9]+',
name: '#string',
type: '<Type>',
meta: #(meta),
integration_id: '#uuid'
}
"""
Get an error similar to this:
KarateException: objects-api.feature:40 - path: $[0].meta.created, actual: '2016-11-30T20:48:16.782Z', expected: '#regex [0-9]+-[0-9]+-[0-9]+[0-9]+:[0-9]+:[0-9]+[0-9]+', reason: regex match failed
Here is a suggestion, why don't you parse the dates into java Date objects and that will open up more possibilities such as being able to compare 2 dates.
Here are good examples:
https://stackoverflow.com/a/54133126/143475
https://stackoverflow.com/a/55938480/143475
That said, I think you missed having to escape the backslash-es, this is a Java thing and is mentioned in the docs. So this works:
* def date = '2017-03-23T14:16:25.854Z'
* match date == '#regex \\d+-\\d+-\\d+.\\d+:\\d+:\\d+.\\d+.'
* match date == '#regex [0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]{3}Z'
The Karate JS engine also supports the RegExp JS object, so see if this example gives you some solutions as well:
https://stackoverflow.com/a/54768838/143475
EDIT, this works for me as well:
* def meta = { created: '#regex \\d+-\\d+-\\d+.\\d+:\\d+:\\d+.\\d+.', modified: '#regex \\d+-\\d+-\\d+.\\d+:\\d+:\\d+.\\d+.' }
* def response = [{ meta: { created: '2017-03-23T14:16:25.854Z', modified: '2018-06-21T05:38:37.978Z' } }]
* match each response == { meta: '#(meta)' }

How to match a string value from the database with an integer value from the response

My feature calls Java to query the database and then compares the results with the response. The results from the Java call returns all values as string. But in the response some of values are integer. So the test fails with the reason: actual value is not a string. I have tried to convert the results to json but that didn't work. If I print out the results, it shows all keys and values are enclosed in double quotes, but there are no double quotes in the error message. I found a similar question in the forum and it was suggested to set the field in the response to '#ignore'. But I want to verify all the fields. How do I get this to work?
Scenario: Get an script by id
* def results = db.getRows("select * from ScriptVersion where id=4 order by version")
Given path '4'
When method get
Then status 200
And match response.version == results
[main] ERROR com.intuit.karate - assertion failed: path: $.version[0], actual: {id=4, version=1, created=2016-06-23T10:49:51.9630000-05:00, updated=2016-06-23T10:49:51.9630000-05:00, message=Initial Version, author=ocadm, hash=0023ad00455962eee4ef1db16a58ce41}, expected: {created=2016-06-23T10:49:51.9630000-05:00, author=ocadm, id=4, message=Initial Version, version=1, updated=2016-06-23T10:49:51.9630000-05:00, hash=0023ad00455962eee4ef1db16a58ce41}, reason: [path: $.version[0].id, actual: 4, expected: '4', reason: actual value is not a string]
Just convert the fields you need to the right data type before the match:
* def results = [{ id: '1', foo: 'bar' }, { id: '2', foo: 'baz' }]
* def fun = function(x){ x.id = ~~x.id; return x }
* def results = karate.map(results, fun)
* match results == [{ id: 1, foo: 'bar' }, { id: 2, foo: 'baz' }]

Karate Schema validation, does it work with nested arrays when using match contains assertion?

I'm trying to use a match on a json object by only checking for some elements. The Json object contains a nested array which seem to be the root cause for the test failing. It seems like using the 'match contains' assertion on key:values within the nested array does not work as expected and throws an error.
I've tried to use 'match contains' on first level keys and that works fine but when attempting to match keys within a nested array I get the following error message: reason: actual value has 1 more key(s) than expected
* def json = { id: 1, priority: 1, compRound: { id: 1, comp_id: 89 } }
* match json contains
"""
{
id: '#number',
priority: '#number',
compRound: {
id: '#number'
}
}
"""
As I'm using the match contains assertion, I would expect for the test to pass but instead it looks like Karate expects all key:values within the nested array to be present.
Please read this section of the docs carefully: https://github.com/intuit/karate#contains-short-cuts
* def json = { id: 1, priority: 1, compRound: { id: 1, comp_id: 89 } }
* def compRound = { id: '#number' }
* match json == { id: '#number', priority: '#number', compRound: '#(^compRound)' }

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)' }