Would appreciate some feedback on this. Triggered by answering a couple of Stack Overflow questions recently - see this one: https://stackoverflow.com/a/55475101/143475
In other words - suppose you want to create a JSON from an Examples row - you have to go through that step of using the angle-brackets like this:
* def payload = { foo: '<foo>', bar: '<bar>' }
Examples:
| foo | bar |
| x | y |
The problem here is that the * def payload line above cannot be moved into a re-usable JSON file. Because it is not the Karate-native embedded expression style. The angle-brackets have to be in-line in the feature, this is a Cucumber thing. This is painful when the payload gets complex.
Proposed Solution: introduce a new keyword to auto-populate a JSON variable with the current row of the Examples table being processed:
* example data
* def payload = { foo: '#(data.foo)', bar: '#(data.bar)' }
Examples:
| foo | bar |
| x | y |
But most of the time the data is un-necessary noise so:
* example
* def payload = { foo: '#(foo)', bar: '#(bar)' }
Examples:
| foo | bar |
| x | y |
Which means now you can do this:
* example
* def payload = read('reusable.json')
Examples:
| foo | bar |
| x | y |
Where reusable.json is in the good old embedded-expression format:
{ "foo": "#(foo)", "bar": "#(bar)" }
The * example on its own is a little weird. Any other ideas ? I am not in favor of auto converting the example-row to variables in scope - because of impact to existing tests.
And something like : * def payload = example ?
This mapping isn't really usefull { "foo": "#(foo)", "bar": "#(bar)" }
Example could be directly mapped into json ?
@LeJeanbono * def payload = example implies that there is a magic variable called example which is not right - yes we have response and friends - but for good reason. and I don't like __example either. I think the mapping to variables in scope is useful - that's what you typically intend when you use Examples IMO
Having this as a option in configure will look more of the karate way. * configure examplesToJSON = true. User can configure it on different scope based on the need.
@babusekaran yeah that's a good suggestion ! thanks
@LeJeanbono I think I now understand what you meant and agree. I'm thinking that the "directly mapped JSON" would be available from the built-in variable __arg which is consistent with how call works:
* match __arg == { foo: 'x', bar: 'y' }
* def temp = { foo: '#(foo)', bar: '#(bar)' }
* match temp == __arg
Examples:
| foo | bar |
| x | y |
just realized - one huge headache here is data-types in Examples: today they all default to string :|
Something like this for data-types ?
Examples:
| foo: string | bar: integer | foobar: date |
| foo | 1 | 2012-07-14 |
@LeJeanbono yeah :) but I'm inclined to leave it the way it is. if follks need complex data type handling - it is always possible using the old <foo> placeholder mechanism combined with * def = { foo: '<foo>' }
BTW this was one of the biggest breaking changes in Cucumber 3 / 4 - and I want to avoid that mess
to avoid breaking existing tests, here's another proposal - to use __eg and __row as the magic variables within Scenario Outline-s
* match __eg == { foo: 'x', bar: 'y' }
* match __row == 0
Examples:
| foo | bar |
| x | y |
Since you may need the convenience of injecting the current row as variable names, we can add a general-purpose JS API karate.def() that will take a given JSON and spray all the keys as variables into the current scope, similar to how "shared scope" works for call:
* def data = { foo: 'x', bar: 'y' }
* eval karate.def(data)
* match foo == 'x'
* match bar == 'y'
so now if you have a JSON file { "foo": "#(foo)", "bar": "#(bar)" }, you can do this:
* eval karate.def(__eg)
* def payload = karate.read('reusable.json')
* match payload == { foo: 'x', bar: 'y' }
Examples:
| foo | bar |
| x | y |
the final problem we need to solve is data-types. maybe the suggestion from @LeJeanbono should be considered after all:
* match __eg == { foo: 'x', bar: 1 }
Examples:
| foo | bar: number |
| x | 1 |
I would stick with the limited JS data types sufficient for JSON marshalling: string (default), number and boolean. If any complex data-type massaging is needed, just do it manually.
This is one of those changes that may tilt things towards feeling "weird" - so would like some feedback.
this is a very interesting one ! finally decided to go with karate.set() and __row and __num for the magic variables. also I decided on a very simple strategy for "type hints" which is to append a ! after the column name.
these three examples should make things clear. all of this should work for dynamic scenario outlines also, but needs testing
Scenario Outline: name is <name> and age is <age>
* def name = '<name>'
* match name == __row.name
* def expected = __num == 0 ? 'name is Bob and age is 5' : 'name is Nyan and age is 6'
* match expected == karate.info.scenarioName
Examples:
| name | age |
| Bob | 5 |
| Nyan | 6 |
Scenario Outline: magic variables with type hints
* def expected = __num == 0 ? { name: 'Bob', age: 5 } : { name: 'Nyan', age: 6 }
* match __row == expected
Examples:
| name | age! |
| Bob | 5 |
| Nyan | 6 |
Scenario Outline: magic variables with embedded expressions
* def expected = __num == 0 ? { name: 'Bob', alive: false } : { name: 'Nyan', alive: true }
* match expected == { name: '#(__row.name)', alive: '#(__row.alive)' }
* eval karate.set(__row)
* match expected == { name: '#(name)', alive: '#(alive)' }
Examples:
| name | alive! |
| Bob | false |
| Nyan | true |
an update the extra * eval karate.set(__row) is no longer necessary, this is done automatically by default, but can be switched off via a configure setting. after writing a few data-driven java unit-tests using karate (yes you read that right, unit-tests :) I felt this was the right thing to do, see example: https://github.com/intuit/karate/blob/develop/karate-demo/src/test/java/demo/unit/cat.feature
released 0.9.3
Most helpful comment
this is a very interesting one ! finally decided to go with
karate.set()and__rowand__numfor the magic variables. also I decided on a very simple strategy for "type hints" which is to append a!after the column name.these three examples should make things clear. all of this should work for dynamic scenario outlines also, but needs testing