Karate array field become object

2019-05-28 14:15发布

问题:

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?

回答1:

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



回答2:

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.



标签: karate