Wanted to try oneof conditon in JSON schema validation in Karate DSL - karate

I've been using something like this.
* def schema =
"""
{
eligible: #string,
Reason: ##string,
enrolled: '##regex ^\\d{4}-\\d{2}-\\d{2}$',
modifiable: ##string,
Date: '##regex ^\\d{4}-\\d{2}-\\d{2}$',
status: #string,
Id: #string,
email: #string,
serviceAddressDetails: ##[] firstSchema,
DeviceIds: #[] #string
}
"""
The expected response has two possible outcomes, I want to assert that if we get either of them, the test should pass.
First,
DeviceIds : ["abcderfg"]
Second
DeviceIds : [
{
id : "abcd"
}
],
If we get either of them in the response, the test/schema should pass. How can I assert both these scenarios in the same schema?
Any help is much appreciated. Thanks!

Just run a second check. I know, it may not feel like a "single reusable schema" but take my advice, it is not worth it. Here's a solution:
* def response1 = { deviceIds: ['abcd'] }
* def firstDevice = response1.deviceIds[0]
* def isTypeOne = karate.typeOf(firstDevice) == 'string'
* def expectedDevice = isTypeOne ? '#[] #string' : '#object'
* match response1 == { deviceIds: '#(expectedDevice)' }
* def response2 = { deviceIds: { id: 'abcd' } }
* def firstDevice = response2.deviceIds[0]
* def isTypeOne = karate.typeOf(firstDevice) == 'string'
* def expectedDevice = isTypeOne ? '#[] #string' : '#object'
* match response2 == { deviceIds: '#(expectedDevice)' }
Other ideas:
https://stackoverflow.com/a/62567262/143475

Related

Karate: How to paste and run scenario for each param from array response inside scenario?

I have a range of values, like taskId, extracted from DB.
* def getTasks = db.readRows('SELECT task_id from tasks t WHERE t.status = \'IN_PROGRES\'
')
* def getIds = get getTasks[*].task_id
* 'task', 'setUser'
* request
"""
[{
"task_id": " ",
"assignedUser": {
"user": "someValue"
}
}
]
"""
* method post
* status 200
* def responseResult =
"""
{
"totalCount": '#number',
"successCount": '#number',
"skippedCount": '#number',
"failedCount": '#number',
}
"""
* match response == responseResult
I need to get each value from the list response and paste in into a "task_id"
Could you please clarify this case?
If you mean trying to create a JSON array from a bunch of values that is easy.
* def response = { foo: 1, bar: 2 }
* def task_ids = []
* task_ids.push(response.foo)
* task_ids.push(response.bar)
* match task_ids == [1, 2]
When it comes to JSON manipulation, think of Karate as just like JavaScript.

Karate Nested Schema contains "Not Required" not working?

i'm trying to understand karat schema fuzsy validation with contains but is not working
if I use ##string for not required it validates required when is null
this is my example
Scenario: Test
* def payload =
"""
{
nested: {
field: 'not required'
}
}
"""
* def payload2 =
"""
{
nested: null
}
"""
* def schema =
"""
{
nested: {
field: '##string'
}
}
"""
* match payload contains schema
* match payload2 contains schema
I get this error in console
path: $.nested, actual: null, expected: {field=##string}, reason: actual value is null
Thanks for help
Pay attention to the structure. I think this is what you are trying:
* def part = { field: '#string' }
* def schema = { nested: '##(part)' }
* def payload = { nested: { field: 'not required' } }
* match payload == schema
* def payload2 = { nested: null }
* match payload2 == schema
And please refer the docs: https://github.com/intuit/karate#schema-validation
yes thanks, also this works
* def schema =
"""
{
'nested.field': '##string'
}
"""

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(_)'

Karate - Exception raises for invalid jsonpath

I have a Json response like below. The difference here is my Json body has a number as the parent node.
def response =
"""
{
"22388043":[
{
"firstName":"Romin",
"lastName":"Irani",
"phoneNumber":"408-1234567",
"emailAddress":"romin.k.irani#gmail.com"
}
]
}
"""
I want to return the mobileNumber attribute value from the response body. In this scenario I don't have that attribute in my response. So here I want to get a null value.
So when I use * def mobile = $.22388043[0].mobileNumber, I'm getting below error.
No results for path: $['22388043'][0]['mobileNumber']
Please advise on this.
Karate does give you a way to get the values of JSON keys.
Hopefully this example answers all your other questions as well:
* def response =
"""
{
"22388043":[
{
"firstName":"Romin",
"lastName":"Irani",
"phoneNumber":"408-1234567",
"emailAddress":"romin.k.irani#gmail.com"
}
]
}
"""
* def id = karate.keysOf(response)[0]
* match id == '22388043'
* def person = response[id][0]
* match person contains { firstName: 'Romin', lastName: 'Irani' }
* match person.mobileNumber == '#notpresent'

Using karate.forEach and karate.set to extract index of value from json array

I have the below json:
{
"id": [
"1A",
"2B"
],
"name": [
"rs",
"mk"
]
}
I want to extract the id value when name is 'rs' or 'mk'. There will be no duplication of name values and the size of id and name keys will always match.
So i have created the following scenario where:
- I iterate through the name array using forEach.
- Find if the value of name matches rs or mk and if it does, retrieve the index.
- Then use this index to find the retrieve the value from id key.
When I run this, the name_rs_idx and name_mk_idx are not being set and are blank.
Scenario: Using forEach and karate.set
* def vals = { id: ['1A', '2B'], name: ['rs', 'mk'] }
* def name_rs_idx = ''
* def rsFun =
"""
function(x, i) {
if(x == 'rs') {
karate.set(name_rs_idx, i);
}
}
"""
* karate.forEach(vals.name, rsFun)
* print 'RS Index - ' + name_rs_idx
* def name_rs_id = vals.id[name_rs_idx]
* print 'RS id -' + name_rs_id
* def name_mk_idx = ''
* def mkFun =
"""
function(x, i) {
if(x == 'mk') {
karate.set(name_mk_idx, i);
}
}
"""
* karate.forEach(vals.name, mkFun)
* print 'MK Index - ' + name_mk_idx
* def name_mk_id = vals.id[name_mk_idx]
* print 'MK id -' + name_mk_id
Maybe i am not using the forEach function correctly or logic is incorrect.
I think you are over-complicating things :) Here is my solution, take time to read and understand it, it will improve your Karate fundamentals ! To simplify things, we first combine the data into an array of { name: '', id: '' } pairs. Then things become much easier.
* def vals = { id: ['1A', '2B'], name: ['rs', 'mk'] }
* def fun = function(x, i){ return { name: vals.name[i], id: vals.id[i] } }
* def pairs = karate.map(vals.name, fun)
* def fun = function(x){ return x.name == 'rs' || x.name == 'mk' }
* def filtered = karate.filter(pairs, fun)