Inside a Karate `Scenario Outline`, what are the `__row` and `__num` variables bound to, and what else does each `Examples:` column give you?
answer
- One holds the row, one holds the index
- Columns arrive as variables of their own
- Counting starts where an array would
- Set up before the first Background step
basics
~10 sIn Karate, __row holds the whole current Examples row as an object, __num holds that row's index counting from zero, and every column is also bound as a variable under its own name.
solid answer
~40 sKarate binds three things for every generated scenario. **`__row`** is the entire current row as an object, so `__row.name` works and `match __row == { name: '#string', age: '#number' }` asserts the whole row at once. **`__num`** is the example index, and it is **zero-based** — the first data row is `0`, not `1`. On top of that each column is **auto-bound as a variable of its own name**, so a `name` column simply gives you `name`, with the type its `!` hint (or lack of one) implies. All three are in place before the first step runs, including the `Background` steps, and they exist for a dynamic single-cell table too, where `__row` is whatever object the expression produced.
code
gherkin · 11 linesFeature: magic variables
Scenario Outline: row and index are both bound
* def expected = [{ name: 'Bob', age: 5 }, { name: 'Nyan', age: 7 }]
* match __row == expected[__num]
* match name == __row.name
Examples:
| name | age! |
| Bob | 5 |
| Nyan | 7 |go deeper
Recall the three bindings: __row for the whole row, __num for its zero-based index, and one variable per column named after the header with any ! stripped.
Explain that they are seeded before the first step so the Background sees them too, and that __row carries converted values rather than the raw cell text.
Use __row as a per-row contract — one match against a marker literal catches missing type hints and stray columns before they become a confusing failure later.
Treat the row-shape assertion as a convention worth mandating: it turns fixture drift into an immediate, local failure instead of a scattered one across many suites.
## Three bindings, set before any step runs When Karate expands a `Scenario Outline` into one scenario per `Examples:` row, it seeds that scenario's JavaScript scope with the row's data before executing a single step. Three things land there: - **`__row`** — the whole row as an object, keyed by column name. - **`__num`** — the row's index, **starting at zero**. - **one variable per column** — a `name` column becomes a variable called `name`. They are Karate's own addition; nothing in plain Gherkin gives a scenario a handle on its own row. Because they are installed at scenario construction, they are visible to the `Background` steps as well as the outline's own steps, which is why `* print 'row', __num` in a `Background` is a legitimate debugging line. ## `__row`, and why the whole-row handle matters The obvious use is dotted access: `__row.name` instead of `name`. The valuable use is asserting the row as a unit: ```gherkin Scenario Outline: shape of every row * match __row == { name: '#string', age: '#number' } Examples: | name | age! | | Bob | 5 | | Nyan | 7 | ``` That one step is a contract on the data, checked once per row. It catches the mistakes that otherwise surface as a confusing assertion three steps later — a missing `!` on a numeric column, a stray trailing column, a CSV field that arrived as text. It also pairs with `__num` for a self-checking fixture: `match __row == expected[__num]` compares each generated row against a literal list built in the feature. `__row` reflects the column **types**, not the raw text. With `| age! |` it holds `{ name: 'Bob', age: 5 }`; without the hint it holds `{ name: 'Bob', age: '5' }`. A blank cell shows up as `null` in `__row` rather than being absent. ## `__num` is zero-based, and that is the trap The first data row is `__num == 0`. Two things make people expect otherwise: 1. The header line looks like a row when you count by eye, so "the first row" is ambiguous in conversation. 2. Reports and run output tend to number examples from one for human consumption, so what you see in a report and what `__num` holds are not the same number. Use `__num` for indexing into a parallel array, for deriving a unique value per row, or for branching on the first or last row — never as a display number. ## The per-column variables Each column key also becomes an ordinary variable. That is what makes an outline read as Karate rather than as Cucumber glue: - `* def cat = read(filename + '.json')` — a `filename` column drives which fixture loads. - `And request { name: '#(name)', age: '#(age)' }` — the embedded-expression form reaches the variables directly, keeping their types. - `* def uniqueEmail = 'user' + __num + '@example.test'` — `__num` gives the per-row salt. The names come from the header with any `!` stripped, so `| age! |` binds `age`. A column named the same as something already in scope shadows it for that scenario, which is worth remembering when a `Background` and a table both define `id`. ## In a dynamic outline All three bindings work identically when the rows come from a single-cell dynamic `Examples:` table: - `__row` is the object the expression produced for that index. - `__num` is that index, still zero-based, and for a generator function it is the same index Karate passed into the function. - Each key of the object becomes a variable, exactly as a column would. That symmetry is the point: an outline can be converted from a hand-written table to `read('cats.json')` or `karate.setup().data` without touching a step, as long as the steps use variables and `__row` rather than angle-bracket placeholders. ## Common mistakes 1. **Treating `__row` as a list of all rows.** It is one row — the current one. 2. **Expecting `__num` to match the report's example number.** It does not; it starts at zero. 3. **Reaching for `__row` when the plain variable would do.** `name` is clearer than `__row.name`; save `__row` for whole-row assertions and for passing the row on wholesale, for instance as the argument to a called feature. 4. **Assuming `__row` is raw text.** It carries the converted values, so a numeric column without a `!` hint is a string in `__row` too.
- Do `__row` and `__num` also exist when a Karate outline's rows come from a dynamic single-cell `Examples:` table?Yes, and identically. `__row` is the object the expression produced for that index, `__num` is the index itself, still zero-based, and every key of the object is bound as its own variable. That symmetry is what lets you swap a hand-written table for `read('cats.json')` without editing a single step.
- Why would you assert `match __row == { name: '#string', age: '#number' }` in a Karate outline?It makes the row's shape a checked contract rather than an assumption. One step per row catches a forgotten `!` type hint, a stray column, or a CSV field that arrived as text — all of which otherwise surface as a puzzling assertion failure further down, on a value that looks correct in the message.
saying these in an interview costs you the question
- Thinks __row holds every row of the Examples table
- Says __num starts at one for the first data row
- Assumes only __row exists and columns are not bound individually
- Expects __num to match the example number printed in a report
- Believes the magic variables are unavailable to Background steps