4

I'm using Karate framework with JUnit.

Using this feature:

Given path 'save_token'
And request
"""
{
  "token": "test_token"
}
"""
And retry until response.tokens ==
"""
[
    "test_token"
]
"""
When method POST

I'm having this exception:

java.lang.ArrayIndexOutOfBoundsException: 1
    at com.intuit.karate.core.MethodMatch.convertArgs(MethodMatch.java:60)
    at com.intuit.karate.core.Engine.executeStep(Engine.java:141)
    at com.intuit.karate.core.ScenarioExecutionUnit.execute(ScenarioExecutionUnit.java:171)

When response.tokens list is empty:

{
    "tokens": []
}

I don't understand why == does not work in this case (it should return false, and keep retrying).

Thanks in advance!

italktothewind
  • 1,950
  • 2
  • 28
  • 55

1 Answers1

13

The retry until expression has to be pure JavaScript and the special Karate match keywords such as contains are not supported, and you can't do a "deep equals" like how you are trying, as that also is not possible in JS.

EDIT: in 0.9.6. onwards you can do a complex match in JS: https://stackoverflow.com/a/50350442/143475

Also note that JsonPath is not supported, which means * or .. cannot appear in the expression.

So if your response is { "tokens": [ "value1" ] }, you can do this:

And retry until response.tokens.includes('value1')

Or:

And retry until response.tokens[0] == 'value1'

To experiment, you can try expressions like this:

* def response = { "tokens": [ "value1" ] }
* assert response.tokens.includes('value1')

At run time, you can use JS to take care of conditions when the response is not yet ready while polling:

And retry until response.tokens && response.tokens.length

EDIT: actually a more elegant way to do the above is shown below, because karate.get() gracefully handles a JS or JsonPath evaluation failure and returns null:

And retry until karate.get('response.tokens.length')

Or if you are dealing with XML, you can use the karate.xmlPath() API:

And retry until karate.xmlPath(response, '//result') == 5

And if you really want to use the power of Karate's match syntax, you can use the JS API:

And retry until karate.match(response, { tokens: '##[_ > 0]' }).pass

Note that if you have more complex logic, you can always wrap it into a re-usable function:

* def isValid = function(x){ return karate.match(x, { tokens: '##[_ > 0]' }).pass }
# ...
And retry until isValid(response)

Finally if none of the above works, you can always switch to a custom polling routine: polling.feature

EDIT: also see this answer for an example of how to use karate.filter() instead of JsonPath: https://stackoverflow.com/a/60537602/143475

Peter Thomas
  • 54,465
  • 21
  • 84
  • 248
  • What kind of object is response.tokens that you can apply contains() method? I was looking for javascript arrays docs and they use includes(). I'm looking for something like containsAll(). – italktothewind Apr 28 '19 at 07:52
  • it is actually a java `List`. maybe you just need to do this `And retry until karate.match(response, { tokens: '#(^^expected)' }).pass` - refer to "contains short-cuts" in the docs – Peter Thomas Apr 28 '19 at 08:02
  • Great. Last question, how can you break retry until expressions? Line was too long and enter does not work. – italktothewind Apr 28 '19 at 08:16
  • I know that I can write a separated function, but I want to put it there in an expression of several lines. – italktothewind Apr 28 '19 at 08:20
  • 1
    @italktothewind you can't. but remember, "open source" ;P – Peter Thomas Apr 28 '19 at 09:06
  • Perhaps worth mentioning that as of 0.9.6, if the response is itself a JSON array, `retry until response.contains(expected)` would not work (it did up to 0.9.5), and should be replaced with `retry until response.includes(expected)`. – Tzach Zohar Mar 17 '22 at 18:28
  • @TzachZohar not sure about that. maybe yes if it is a primitive (e.g. string) or an array of primitives – Peter Thomas Mar 17 '22 at 19:02