Can I use the ## operation on JSON objects? - karate

Looking at the documentation it is straighforward how to use the ## operator on fields e.g
* def data = { a: 'hello', b: null, c: null }
* def json = { foo: '#(data.a)', bar: '#(data.b)', baz: '##(data.c)' }
* match json == { foo: 'hello', bar: null }
but what if I want to use it on a json object when there are no properties ? E.g. if I am doing something like
* def data = { a: 'hello', b: null, c: null }
* def json = { foo: '#(data.a)', bar: '#(data.b)', jsonObject: {baz: '##(data.c)'} }
* match json == { foo: 'hello', bar: null }
it is complaining that there is an empty object
actual: {foo=hello, bar=null, jsonObject={}}, expected: {foo=hello, bar=null}
and putting ##{baz: '##(data.c)'} or ##({baz: '##(data.c)'}) as jsonObject value does not work as ## is not recognized correctly.
What is the correct syntax?Or is there another way to do what I am describing?

This is actually a somewhat complex conditional substitution. Here's how you can do it:
* def data = { a: 'hello', b: null, c: null }
* def temp = data.c ? { baz: data.c } : null
* def json = { foo: '#(data.a)', bar: '#(data.b)', jsonObject: '##(temp)' }
* match json == { foo: 'hello', bar: null }
Maybe you are over-complicating things, and all you need is this:
* def data = { a: 'hello', b: null, c: null }
* def json = { foo: '#(data.a)', bar: '#(data.b)' }
* if (data.c) json.jsonObject = ({ baz: data.c })
* match json == { foo: 'hello', bar: null }

Related

Karate test - any way to do an "or" match in "match each"?

I have something like the following. Is it possible to get karate to do an "or" match for foo and bar?
Meaning - foo starts with fooStartWithChar OR bar starts with barStartWithChar
And match each response ==
"""
{
foo: '#? { _.charAt(0) == fooStartWithChar}',
bar: '#? { _.charAt(0) == barStartWithChar}',
}
"""
Sometimes plain old JS (+Java) is your friend:
* def response = [{ foo: 'aa', bar: 'bb' }, { foo: 'ax', bar: 'by' }]
* def isValid = function(x){ return x.foo.startsWith('a') || x.bar.startsWith('b') }
* match each response == '#? isValid(_)'
* def nameStartsWith = function(x) { return x.foo.charAt(0) == fooStartWithChar || x.bar.charAt(0) == barStartWithChar}
And match each response == '#? nameStartsWith(_)'

How to assert a json property which can be either null(Porp:null) or has a sub schema(Porp:{ denyAny: '#boolean', assertions: '#[]' }) In KARATE DSL?

I have a Json payload to validate. And It has a property which can be either null or a sub json object. But this property exists in the json.
I tried following methods:
01
And def dnyAssertionSchema = { denyAny: '#boolean', assertions: '##[]' }
And match each policyGResponse ==
"""
{
denyAssertions: '##(dnyAssertionSchema)'
}
"""
AND
And match each policyGResponse ==
"""
{
denyAssertions: '##null dnyAssertionSchema'
}
"""
AND
This does not work as the property is not an array so I tried above second method even I couldn't find an example as such.
And match each policyGResponse ==
"""
{
denyAssertions: '##[] dnyAssertionSchema'
}
"""
The Actual response can be either
{
denyAssertions=null
}
OR
{
denyAssertions={ denyAny: true, assertions: ["a","b"] }
}
I use Karate 0.9.1
Error message I get is 'reason: actual value has 1 more key(s) than expected: {denyAssertions=null}' in first try
In second try I get 'assertion failed: path: $[3].denyAssertions, actual: {denyAny=false, assertions=[]}, expected: '##null dnyAssertionSchema', reason: not equal'
Your JSON is still not valid but anyway. Here you go:
* def schema = { denyAny: '#boolean', assertions: '#[]' }
* def response1 = { denyAssertions: { denyAny: true, assertions: ["a","b"] } }
* match response1 == { denyAssertions: '##(schema)' }
* def response2 = { denyAssertions: null }
* match response1 == { denyAssertions: '##(schema)' }

karate: finding index of the particular element value from API response

my code for finding index as below
* def list = nestActual #this is API response value which is given at the end
* def searchFor = { category_name: 'books3'}
* def foundAt = []
* def fun = function(x, i){ if (karate.match(x, searchFor).pass) foundAt.add(i) }
* eval karate.forEach(list, fun)
* print "==========foundAt=======" +foundAt
i have tried the above code for finding index where im getting foundAt index as null.
Below is my response where i want to find index of "category_name":"books3"
[
{
"category_id":1, "parent_cat_id":0, "category_name":"books", "slug_name":"books_1", "popular":true,
}, {
"category_id":2, "parent_cat_id":1, "category_name":"books2", "slug_name":"books_2", "popular":false,
}, {
"category_id":3, "parent_cat_id":1, "category_name":"books3", "slug_name":"books3_2", "popular":false,
}, {
"category_id":4, "parent_cat_id":3, "category_name":"mp3", "slug_name":"mp_3", "popular":false, }, {
"category_id":5, "parent_cat_id":3, "category_name":"mp4", "slug_name":"humoristiska_deckare_mysi_deck_3", "popular":false, }, {
"category_id":6, "parent_cat_id":3, "category_name":"video", "slug_name":"video3", "popular":false,
} ]
Please let me know how to find index of "category_name":"books3" using karate
Guess what, there is a far simpler way, the trick is to convert your search target into an array of primitives. Then you can use the List.indexOf() Java method:
Scenario: using the java indexOf api (will change with graal)
* def response = [{ name: 'a' }, { name: 'b' }, { name: 'c' }]
* def names = $[*].name
* def index = names.indexOf('b')
* match index == 1

Check if an array contains a specific object only once

Given the following input:
* def response = [{ a: 1 }, { a: 2 }]
* def item = { a: 1 }
How to check that item is present only once in response?
There's no direct way to do this, since less common. You can do this in 2 steps by filtering the list and then using contains only.
* def response = [{ a: 1 }, { a: 2 }]
* def item = { a: 1 }
* match response contains item
* def fun = function(x){ return karate.match(x, item).pass }
* def filt = karate.filter(response, fun)
* match filt contains only item

Karate API : Converting two arrays into an object

How to merge to below arrays into an object in Karate API. I tried below code it is not working.
keys = ['foo', 'bar', 'qux']
values = ['1', '2', '3']
Feature: ArrayToObject
Scenario: ArrayToObject Coversion JS script
* def keys = ['foo', 'bar', 'qux']
* def values = ['1', '2', '3']
* def Arr2object =
"""
function (keys, vals) {
return keys.reduce(
function(prev, val, i) {
prev[val] = vals[i];
return prev;
}, {}
);
}
"""
* string text = Arr2object(keys, values)
* print text
Expected something like this
{
"foo": "1",
"bar": "2",
"qux": "3"
}
This might work,
* def Arr2object =
"""
function(keys,values){
var newObj = {};
if(keys.length == values.length){
for (var i = 0; i <= keys.length - 1; i++) {
newObj [keys[i]] = values[i];
}
return newObj;
}
return newObj;
}