I've got this really weird problem.
I want to append two lists into one list, but it seems like Karate does not support this function, so I write a JS function.
function(lists){
var arr = []
for each (var list in lists){
for each (var item in list){
arr.push(item);
}
}
return arr;
}
And I write this feature file for testing:
Feature:
Scenario:
* def appendList = read('../utils/append-list.js')
* def arr = [{a: 'a'}, {b: 'b'}, {c: 'c'}]
* def arr1 = [{a: 'a'}, {b: 'b'}, {c: 'c'}]
* def arr2 = appendList([arr, arr1])
* print 'arr2', arr2
* def xx = { aa: '#(arr2)' }
* print 'xx', xx
* copy yy = xx
* print 'yy', yy
* match xx == yy
Here's the log:
16:00:28.424 [main] INFO com.intuit.karate - [print] arr2 [
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
},
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
}
]
16:00:28.444 [main] INFO com.intuit.karate - [print] xx {
"aa": {
"0": {
"a": "a"
},
"1": {
"b": "b"
},
"2": {
"c": "c"
},
"3": {
"a": "a"
},
"4": {
"b": "b"
},
"5": {
"c": "c"
}
}
}
16:00:28.466 [main] INFO com.intuit.karate - [print] yy {
"aa": [
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
},
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
}
]
}
16:00:28.469 [main] ERROR com.intuit.karate - assertion failed: path: $.aa, actual: [object Array], expected: [{"a":"a"},{"b":"b"},{"c":"c"},{"a":"a"},{"b":"b"},{"c":"c"}], reason: actual value is not list-like
com.intuit.karate.exception.KarateException: path: $.aa, actual: [object Array], expected: [{"a":"a"},{"b":"b"},{"c":"c"},{"a":"a"},{"b":"b"},{"c":"c"}], reason: actual value is not list-like
at com.intuit.karate.StepDefs.matchNamed(StepDefs.java:540)
at com.intuit.karate.StepDefs.matchEquals(StepDefs.java:526)
at ✽.* match xx == yy(kromotus/test/test.feature:12)
com.intuit.karate.exception.KarateException: path: $.aa, actual: [object Array], expected: [{"a":"a"},{"b":"b"},{"c":"c"},{"a":"a"},{"b":"b"},{"c":"c"}], reason: actual value is not list-like
at com.intuit.karate.StepDefs.matchNamed(StepDefs.java:540)
at com.intuit.karate.StepDefs.matchEquals(StepDefs.java:526)
at ✽.* match xx == yy(kromotus/test/test.feature:12)
I don't understand why sometimes the array is the array, sometimes it becomes object?
You can try to convert the return data in JavaScript function to JSON. Below is explain what happening in karate as my understand:
* def appendList = read('../utils/append-list.js')
* def arr = [{a: 'a'}, {b: 'b'}, {c: 'c'}]
* def arr1 = [{a: 'a'}, {b: 'b'}, {c: 'c'}]
* def arr2 = appendList([arr, arr1]) # return JS[] variable type instead of JSON object
* print 'arr2', arr2
* def xx = { aa: '#(arr2)' }
* print 'xx', xx
* copy yy = xx
* print 'yy', yy
* match xx == yy
Below is how to fix it:
* def appendList = read('append-list.js')
* json arr = [{a: 'a'}, {b: 'b'}, {c: 'c'}]
* json arr1 = [{a: 'a'}, {b: 'b'}, {c: 'c'}]
* json arr2 = appendList([arr, arr1]) # convert to JSON
* print 'arr2', arr2
* json xx = { aa: '#(arr2)' }
* print 'xx', xx
* copy yy = xx
* print 'yy', yy
* match xx == yy
Log Info:
20:37:02.034 [main] WARN com.intuit.karate - skipping bootstrap configuration: could not find or read file: karate-config.js, prefix: CLASSPATH
20:37:02.139 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.142 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.149 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.149 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.168 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.169 [main] INFO com.intuit.karate - [print] arr2 [
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
},
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
}
]
20:37:02.173 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.177 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.178 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $['aa']
20:37:02.179 [main] DEBUG com.jayway.jsonpath.internal.JsonContext - Set path $['aa'] new value [{a=a}, {b=b}, {c=c}, {a=a}, {b=b}, {c=c}]
20:37:02.182 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.183 [main] INFO com.intuit.karate - [print] xx {
"aa": [
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
},
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
}
]
}
20:37:02.188 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.188 [main] INFO com.intuit.karate - [print] yy {
"aa": [
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
},
{
"a": "a"
},
{
"b": "b"
},
{
"c": "c"
}
]
}
20:37:02.189 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
20:37:02.189 [main] DEBUG com.jayway.jsonpath.internal.path.CompiledPath - Evaluating path: $
1 Scenarios ([32m1 passed[0m)
10 Steps ([32m10 passed[0m)
0m0.680s
html report: (paste into browser to view)
Result
Yes, by default, JSON is internally converted to Java lists which can be confusing. Best practice is to use 'Java style' wherever possible. And when iterating, use for-loops with indexes. For example, try these:
* def first = [{a: 1}, {b: 2}]
* def second = [{c: 3}, {d: 4}]
* eval first.addAll(second)
* print first
This has the same effect as the above, but is more convoluted:
* def append = function(f, s){ for (var i = 0; i < s.length; i++) { f.add(s[i]) }; return f }
* def first = [{a: 1}, {b: 2}]
* def second = [{c: 3}, {d: 4}]
* def result = append(first, second)
* print result
Note that we use the Java List.add()
and List.addAll()
methods, not the JS push
. You can of course write your custom function that does this behind the scenes.
BTW the next version of Karate will introduce karate.forEach
and even map
and filter
operations to make iterating easier. Maybe we need a karate.append()
and karate.merge()
(for merging 2 JSON objects). Feel free to raise a feature request.