I am making a post call and after post call the object goes into array at random position so how can I assert for that object.without knowing it's response. message[position?].
Please suggest me what can I do.
Use JsonPath. Here is one example: https://stackoverflow.com/a/64373344/143475
Keep in mind there are plenty of ways to extract data from JSON that ignores the exact path or position. For example:
* def response = { a: 1, b: 2, c: 3, d: { a: 1, b: 2, e: 5 } }
* def extracted = $..e
* match extracted contains 5
If you are just concerned about arrays, use contains: https://github.com/karatelabs/karate#match-contains
Related
I would like to print the index value for matching JSON data using Karate.
For below code the expected answer should be 2, but I got -1, not sure what I am missing
[
{"aaa": 101},
{"bbb": 102},
{"ccc": 103}
]
Feature: Rough
Scenario: Rough
* def myData = read('roughTestData.json')
* print "Index ->>", myData.indexOf('ccc')
You have a JSON object within an array (not a plain string), and the complication here is you are interested in keys not values, so you need to do this instead:
* def response = [{"aaa":101},{"bbb":102},{"ccc":103}]
* def keys = response.map(x => Object.keys(x)[0])
* def index = keys.indexOf('ccc')
* match index == 2
We are using JS array methods such as map(), Object.keys() and indexOf() above
That said, I personally think you are over-complicating things because Karate allows you to match without caring about the order. For example:
* def response = [{"aaa":101},{"bbb":102},{"ccc":103}]
* def expected = [{ccc:'#number'},{bbb:'#number'},{aaa:'#number'}]
* match response contains only expected
So read the docs, and be creative: https://github.com/karatelabs/karate#json-arrays
I'm trying to write some karate tests that check a response for some expected structure. For example, there is an object that looks like this:
{
'true|true':'disabled',
'true|false':'locked',
'false|false':'enabled',
'false|true':'disabled',
'default':'enabled'
}
However, the keys and values could be any strings. There must be a way to specify this, but I'm at a loss for how to do this in a generic way.
Essentially, what I would like to test is that 1) the object is not empty, 2) each of the keys and values is a string (not an object and not null).
The basic match looks like this:
* def objects = $fields..objects
And match each objects ==
"""
{
key1: '#? isString(_)',
key2: '#? isString(_)'
}
"""
It looks like the names of the keys must be known in advance, so perhaps match expressions are not the appropriate way to test this.
You can extract all keys like this:
* def keys = karate.keysOf(object)
So once you have that, you are in business:
* def foo = { a: 1, b: 2 }
* def keys = karate.keysOf(foo)
* match each keys == '#string'
* assert keys.length > 0
See JSON tranforms for other ideas, e.g. karate.forEach(): https://github.com/intuit/karate#json-transforms
Is it possible to get the index value of a
* match response.Services[*] contains { "Service": "xyz"}
I'm looking to reuse it later for more specific tests on the same Service object.
Theres reference to a __loop variable here but I guess I don't really understand how to apply it.
I love your questions, you are certainly pushing Karate to the limits and I'm having to think hard to answer these as well :)
The match does not allow you to get the "found index" or even the "loop position" which come to think of it - may be actually relevant for a match each - but I digress.
This will take a few extra lines of code, and I really think you need to upgrade to 0.8.0. If it helps, I can confirm that there are no breaking changes (unless you use the stand-alone JAR).
The new karate.forEach() and karate.match() functions allow you to do very complex operations on arrays. Here are 2 examples:
Scenario: karate find index of first match (primitive)
* def list = [1, 2, 3, 4]
* def searchFor = 3
* def foundAt = []
* def fun = function(x, i){ if (x == searchFor) foundAt.add(i) }
* eval karate.forEach(list, fun)
* match foundAt == [2]
Scenario: karate find index of first match (complex)
* def list = [{ a: 1, b: 'x'}, { a: 2, b: 'y'}, { a: 3, b: 'z'}]
* def searchFor = { a: 2, b: '#string'}
* def foundAt = []
* def fun = function(x, i){ if (karate.match(x, searchFor).pass) foundAt.add(i) }
* eval karate.forEach(list, fun)
* match foundAt == [1]
If you really can't upgrade, you can write a function to manually loop over the array and the above examples should get you a solution.
I was trying to pass the variable 'i' value to a array index 'locations[i]' using below karate code. but throwing an error saying unable to parse. Please suggest be for any changes.
Feature: Verify Branches
Background: For loop implementation
Given url ''
When method GET
Then status 200
* def i = 0
* def z = $.locations[i].zip
* def p = $.locations[i].phone
* def fun =
"""
function(locations){
for (var i = 0; i < locations.length; i++)
{
print(i)
print('Element at Location ' + i +':' + p)
}
}
"""
Scenario: Validate the locations
Given url ''
When method GET
Then status 200
* call fun p
It is hard to make out anything since you have not provided the value of the response. There are many things wrong here. But I'll try.
Take this line:
* def z = $.locations[i].zip
This will not work, Karate does not support variables within JsonPath by default, refer the docs: https://github.com/intuit/karate#jsonpath-filters
And I think you are un-necessarily using JsonPath where normal JavaScript would have been sufficient:
* def z = response.locations[i].zip
Also it seems you are just trying to loop over an array and call a feature. Please refer to the documentation on Data Driven Features.
Take some time and read the docs and examples please, it will be worth your time. One more tip - before I leave you to understand Karate a little better. There is a way to convert a JSON array into another JSON array should you need it:
* def fun = function(x){ return { value: x } }
* def list = [1, 2, 3]
* def res = karate.map(list, fun)
* match res == [{ value: 1 }, { value: 2 }, { value: 3 }]
So there should never be a need for you to manually do a for loop at all.
I'm trying to persist a groovy map to a file. My current attempt is to write the string representation out and then read it back in and call evaluate on it to recreate the map when I'm ready to use it again.
The problem I'm having is that the toString() method of the map removes vital quotes from the values of the elements. When my code calls evaluate, it complains about an unknown identifier.
This code demonstrates the problem:
m = [a: 123, b: 'test']
print "orig: $m\n"
s = m.toString()
print " str: $s\n"
m2 = evaluate(s)
print " new: ${m2}\n"
The first two print statements almost work -- but the quotes around the value for the key b are gone. Instead of showing [a: 123, b: 'test'], it shows [a: 123, b: test].
At this point the damage is done. The evaluate call chokes when it tries to evaluate test as an identifier and not a string.
So, my specific questions:
Is there a better way to serialize/de-serialize maps in Groovy?
Is there a way to produce a string representation of a map with proper quotes?
Groovy provides the inspect() method returns an object as a parseable string:
// serialize
def m = [a: 123, b: 'test']
def str = m.inspect()
// deserialize
m = Eval.me(str)
Another way to serialize a groovy map as a readable string is with JSON:
// serialize
import groovy.json.JsonBuilder
def m = [a: 123, b: 'test']
def builder = new JsonBuilder()
builder(m)
println builder.toString()
// deserialize
import groovy.json.JsonSlurper
def slurper = new JsonSlurper()
m = slurper.parseText('{"a": 123, "b": "test"}')
You can use myMap.toMapString()