Karate match each on response assertion is failing to identify missing keys in response - karate

I have a match each assertion like below in my code. Just tried creating similar examples as my code, just to explain the issue.
Scenario: Example scenario 1
* def response =
"""
[
{
id: 1,
name: "John",
password: "abc123"
},
{
id: 2,
name: "David",
password: "abc123"
},
{
id: 3,
name: "Mike",
password: "abc123"
},
{
id: 4,
name: "Johny"
}
]
"""
* match each response[*].password contains 'abc123'
Test status : Pass
Password field is missing in object 4(where id=4). Above test is passing for me. I am expecting Karate to fail the test in this case. How can I make my test fail in this case?
Scenario: Example scenario 2
* def response =
"""
[
{
id: 1,
name: "John",
},
{
id: 2,
name: "David",
},
{
id: 3,
name: "Mike",
},
{
id: 4,
name: "Johny"
}
]
"""
* match each response[*].password contains 'abc123'
Test status : Pass
Here, there is no password field at all in response. But my test is passing.
Need a work around to fail these kind of scenarios.
Example 3 :
* def response =
"""
[
{
id: 1,
name: "John",
password: "abc123",
skills :[ "training", "management"
]
},
{
id: 2,
name: "David",
password: "abc123",
skills :[ "training", "management"
]
},
{
id: 3,
name: "David",
password: "abc123",
skills :[ "training", "coding"
]
},
{
id: 4,
name: "David",
password: "abc123",
skills :[ "training", "management"
]
}
]
"""
Considering * match each response contains { password: 'abc123' } format(mentioned by #peter) to check example 1 and 2, what if I want to check skills array having 'training' in each object under response? How can I achieve this?

you can use match each to validate the json schema
https://github.com/intuit/karate#match-each

Note that response[*].password is a JsonPath expression that will return an array of all the password key-values found and will return only 3 in your case.
What you are looking for is this:
* match each response contains { password: 'abc123' }

Related

Karate - Nested JSON object schema validation causes KarateException

Feature: Test Karate schema validation
Scenario: Test nested json objects
* def response = read('tasks.json')
* def schema = { ab: "##[] string", c: "##[] string" }
* match response ==
"""
{
id: '#string',
name: '#string',
obj1: '#(schema)' ,
obj2: '##(schema)' ,
obj3: '#(schema)' ,
obj4: '#null'
}
"""
Following is json file used (tasks.json)
{
"id": "ad:p2:53456:4634:yu",
"name": "name",
"obj1": {
"ab": [
"test"
],
"c": null
},
"obj2": null,
"obj3": {
"ab": [
"tester"
],
"c": [
"t1", "t2"
]
},
"obj4": null
}
Error: com.intuit.karate.exception.KarateException: javascript evaluation failed: string, ReferenceError: "string" is not defined in at line number 1
I have tried multiple ways like :
obj1: '#(^schema)',
obj1: '#object schema'
but not able to fix the issue.
It should be ##[] #string , read the docs: https://github.com/intuit/karate#schema-validation

String replacement inside json not working with complex objects

I have this Javascript object:
{ person: { name: "john", age: 32 }}
And an endpoint returning:
{
"name": "john",
"age": 32
}
I have this steps:
Given path 'endpoint/'
When method get
Then status 200
And match response ==
"""
{
"name": #(person.name),
"age": #(person.age)
}
"""
This is not working because #(person.name) and #(person.age) both evaluate to null, how can I fix it? (putting them in a new variable is not the fix I'm looking for)
Here you go:
* def data = { person: { name: 'john', age: 32 } }
* def response = { name: 'john', age: 32 }
* match response == data.person
Since you appear to be confused, let me add this (but not recommended because of the above):
* match response == { name: '#(data.person.name)', age: '#(data.person.age)' }

Deep assertion on JSON array with contains in Karate

Scenario:
Given def cat =
"""
{
name: 'Billie',
kittens: [
{ id: 23, name: 'Bob', age: 35 },
{ id: 42, name: 'Wild', age: 25 }
]
}
"""
Then match cat.kittens contains [{ id: 42, name: 'Wild' }, { id: 23, name: 'Bob' }]
As seen in the example, I am not worried about 'age'. I understand that I can use '#ignore'. Is there any other way, if I have lot of such nodes that I have to ignore.
I am just thinking, can it work with JSON arrays as it does with JSON Objects. asserting only specified nodes.
error:
assert.feature:24 - path: $.kittens[*], actual: [{"id":23,"name":"Bob","age":35},{"id":42,"name":"Wild","age":25}], expected: {id=42, name=Wild}, reason: actual value does not contain expected
EDIT:
I tried something suggested in Karate - how to check if array contains values?
but that did not help me. I am not looking for schema-validation instead I am looking for functional validation where every object may have different values for keys.
Below one fails
Scenario:
Given def cat =
"""
{
name: 'Billie',
kittens: [
{ id: 23, name: 'Bob', age: 35 },
{ id: 42, name: 'Wild', age: 25 }
]
}
"""
* def expected = [{ id: 42, name: 'Wild' }, { id: 23, name: 'Bob' }]
Then match cat.kittens contains '#(^expected)'
This one works fine but this is not helping me.
Scenario:
Given def cat =
"""
{
name: 'Billie',
kittens: [
{ id: 23, name: 'Bob', age: 35 },
{ id: 42, name: 'Wild', age: 25 }
]
}
"""
* def expected = { id: 42, name: 'Wild' }
Then match cat.kittens contains '#(^expected)'
Currently, I am reading arrays separately and asserting them with loop.
Just adding the word deep works in 0.9.6.RC4
Then match cat.kittens contains deep [{ id: 42, name: 'Wild' }, { id: 23, name: 'Bob' }]
I don't understand why the linked answer when I closed your first question did not help you. Maybe I don't understand, and someone else can provide a better answer.

How to delete a jsonb item in a nested psql array

I have a table users:
`CREATE TABLE users(
id SERIAL PRIMARY KEY NOT NULL,
username TEXT UNIQUE,
saved_articles JSONB[],
)`
I added a user like so:
"INSERT INTO users (username, saved_articles) VALUES (?, array[]::jsonb[]) RETURNING id, username, saved_articles"
After adding some articles I have this data shape:
{ id: 1,
username: 'test',
saved_articles:
[ { url: 'test',
title: '',
author: '',
source: '',
content:"",
urlToImage: ''
},
{ url: 'not-test',
title: '',
author: '',
source: '',
content:"",
urlToImage: ''
}
]
}
I want to be able to delete a specific item from the saved_articles array based on the url value.
For example, if my url value is 'test', after running the query my data should look like:
{ id: 1,
username: 'test',
saved_articles:
[ { url: 'not-test',
title: '',
author: '',
source: '',
content:"",
urlToImage: ''
}
]
}
First of all, the format of JSONB columns's value should be fixed. That might be tested through CASTing AS JSONB by a SELECT statement such as
SELECT '{ "id": "1",
"username": "test",
"saved_articles":
[ { "url": "test",
"title": "",
"author": "",
"source": "",
"content":"",
"urlToImage": ""
},
{ "url": "not-test",
"title": "",
"author": "",
"source": "",
"content":"",
"urlToImage": ""
}
]}'::jsonb
whether returns error or not.
Then, remove the desired element from the array by use of jsonb_array_elements(json_data -> 'saved_articles') function together with ->> 'url' != 'test' criteria.
And then reconstruct the array by remaining elements by using jsonb_build_array and jsonb_object_agg.
At the last step concatenate the part which doesn't contain that individual array extracted by json_data #- '{saved_articles}' :
SELECT js0||jsonb_object_agg( 'saved_articles', js1 ) AS "Result JSONB"
FROM
(
SELECT json_data #- '{saved_articles}' AS js0, jsonb_build_array( js ) AS js1
FROM tab
CROSS JOIN jsonb_array_elements(json_data -> 'saved_articles') js
WHERE js ->> 'url' != 'test'
) q
GROUP BY js0
Demo

How to validate response value containing the number sign #

My response looks like this:
[
{
"id": 1,
"name": "TEST FORMAT",
"value": "#####"
}
]
I want to validate it like this:
And match response[0] == { id: 1, name: 'TEST FORMAT', value: '#####' }
But it gives me the error below:
ERROR com.intuit.karate - assertion failed: path: $[0].value, actual: '#####', expected: '#####', reason: unknown validator
Actually Karate treats strings that start with # as special and 99% of the time you won't be affected by it.
Anyway, here is the workaround:
* def response = [ { "id": 1, "name": "TEST FORMAT", "value": "#####" } ]
* match response[0] == { id: 1, name: 'TEST FORMAT', value: '#? _ == "#####"' }
* match response[0] == { id: 1, name: 'TEST FORMAT', value: '#regex #{5}' }
I'm actually fixing this right now so that it should work as expected in future versions without needing the workaround.