skip to content

In a Karate feature file the step prefix (`*`, `Given`, `Then`) selects nothing, so what actually decides what `* retry until responseStatus == 200` does - and why does `* cats[id] = request` run as JavaScript instead?

level: middleimportance: must knowfreq 61%

answer

  1. Look past the prefix
  2. The first token decides
  3. Some keywords are two words
  4. Punctuation sends the line elsewhere
  5. Form, multipart, soap, retry take a space

basics

~20 s

The leading keyword after the prefix decides. Karate reads a one- or two-word keyword - retry until is two words - and runs its built-in. When that first token carries punctuation instead, the whole line is evaluated as JavaScript.

solid answer

~60 s

Dispatch is on the first token after the prefix, never on the prefix. That token is matched against Karate's fixed built-in vocabulary, which is mostly one word (`def`, `url`, `path`, `match`, `status`, `method`, `call`, `configure`) plus nine two-word keywords: `form field`, `form fields`, `multipart field`, `multipart fields`, `multipart file`, `multipart files`, `multipart entity`, `soap action` and `retry until`. Only four first words - `form`, `multipart`, `soap` and `retry` - may read past a space and stay part of the keyword, which is why `retry until responseStatus == 200` is one keyword plus one expression. There is a third path: when that first token is punctuated, Karate skips the keyword match entirely and evaluates the line as JavaScript against the scenario's variables. Which characters count is version-scoped - Karate 2.x recognises exactly `.` `(` `)` `[` `]` `'` and `"`, while Karate 1.x is broader and takes almost any non-word, non-whitespace character, so `* count=count+1` runs as JavaScript on 1.x and fails on 2.x. Stay in the subset both lines accept - a dotted call, a parenthesis or a bracket, as in `cats[id]` or `karate.log(x)`.

code

gherkin · 10 lines
gherkin
Feature: what decides what a step does

Scenario: one prefix, two dispatch paths
  # first token is the keyword 'def'
  * def cats = {}
  # first token is punctuated, so the line is JavaScript
  * cats['c1'] = { name: 'Billie' }
  * cats.total = 1
  # keyword path again
  * match cats == { c1: { name: 'Billie' }, total: 1 }

go deeper

for a junior

Remember that the word right after the prefix is the keyword. Being able to point at def in a step reading asterisk def id = 5 and say that is what runs is enough at this level.

for a middle

Explain all three paths: a one-word keyword, one of the nine two-word keywords, or a punctuated first token that sends the whole line to the JavaScript engine. Naming the two-word set is what shows real familiarity.

for a senior

When a step misbehaves, work out which of the three paths it took before changing anything. A mistyped keyword and a line that quietly became JavaScript produce very different symptoms once you know to look at the first token.

for a principal

The design is a closed keyword set plus one escape hatch. It deletes an entire glue layer, and the price is that every extension your teams write lands in the JavaScript path rather than in reviewable, typed code.

## The step, taken apart A Karate step line is prefix, then keyword, then the rest: ``` * retry until responseStatus == 200 ^ ^~~~~~~~~~~ ^~~~~~~~~~~~~~~~~~~~~ | | the expression handed to the keyword's built-in | the keyword — here two words the prefix, which selects nothing ``` The prefix is discarded before anything is decided. What is left is a keyword matched against Karate's fixed built-in set, and the remainder of the line as that keyword's argument. ## Path one: a one-word keyword Most of the vocabulary is a single word — `def`, `set`, `text`, `json`, `xml`, `csv`, `yaml`, `copy`, `table`, `replace`, `match`, `assert`, `print`, `url`, `path`, `param`, `params`, `header`, `headers`, `cookie`, `cookies`, `request`, `method`, `status`, `call`, `callonce`, `eval`, `configure`, `doc`, `driver`. The first word is matched against that set and the built-in for it runs. ## Path two: a two-word keyword Nine keywords are two words long: - `form field` and `form fields` - `multipart field`, `multipart fields`, `multipart file`, `multipart files`, `multipart entity` - `soap action` - `retry until` Only four first words — `form`, `multipart`, `soap` and `retry` — are permitted to read past a space and keep going. That is why `retry until responseStatus == 200` parses as one keyword plus one expression rather than as the keyword `retry` followed by stray words, and why no other keyword can accidentally grow a second word. It is also why you cannot invent `match deep` or `def global` as keywords: the parser will never look past the space for them. ## Path three: a punctuated first token If the first token after the prefix carries punctuation — on Karate 2.x exactly `.` `(` `)` `[` `]` `'` and `"`, on Karate 1.x almost any non-word, non-whitespace character — Karate does not attempt the keyword match at all. It evaluates the whole line as JavaScript, against the scenario's current variables: | step | first token | what happens | |---|---|---| | `* def cats = {}` | `def` | keyword path — assigns a variable | | `* cats['c1'] = 1` | `cats[` | JavaScript — mutates the existing map | | `* cats.total = 1` | `cats.total` | JavaScript — sets a property | | `* karate.log(x)` | `karate.log(` | JavaScript — calls the function | | `* count = count + 1` | `count` | neither — a bare word is read as a keyword and fails | | `* myFunc ('world')` | `myFunc` | JavaScript on 2.x — see below; fails on 1.x | That `count` row is the one that catches people. Punctuation is what buys you the JavaScript path, and a bare identifier followed by a space does not qualify — with one exception, on the 2.x line only. There, an unrecognised first token whose remaining text starts with `(` is evaluated as JavaScript rather than rejected, which is why `* myFunc ('world')` calls the function you defined with `def` and why Karate 2.x's own test suite asserts that step passes. `count = count + 1` has no parenthesis after the bare word, so it gets no such reprieve on either line: it is read as an attempt to use a keyword named `count` and fails. Write `* def count = count + 1` instead, or punctuate the target (`* obj.count = obj.count + 1`). ## Why the third path exists Karate has no step-definition registry to extend, so the keyword set is closed. The punctuated path *is* the extension point: it is how you call a JavaScript function you defined with `def`, how you reach into a Java class, and how you mutate a structure you already built. Karate 2.x widens the gate slightly for the commonest case, the call: an unrecognised keyword followed directly by `(` is evaluated too, so `* myFunc('world')` and `* myFunc ('world')` both run there, while 1.x accepts only the first of those. Karate also offers `eval` as an explicit way onto the same path, which is what you use when the expression's own first token has no punctuation of its own. ## The order matters Read a strange step in this order and the diagnosis falls out: 1. Strip the prefix — it never mattered. 2. Is the first token punctuated? Then the line is JavaScript and the failure is a JavaScript failure: an undefined variable, a bad property, a thrown error. 3. Otherwise, is the first token (or first two) in the built-in set? If yes, the failure is inside that built-in *on Karate 2.x* — a bad match, a failed request, a malformed expression. That inference does not hold on 1.x, where the keyword and the shape of its argument are checked by one and the same pattern: a real keyword carrying a malformed argument (`* status abc`, `* def foo`) matches nothing at all and reports as an unresolved step rather than as a failure inside `status` or `def`. 4. If neither, the keyword is simply not one Karate has, and the step fails for that reason. ## What this rules out Nobody registers keywords. There is no glue path to configure, no expression syntax to learn for binding sentences to methods, and no snippet printed when a step will not resolve — Karate has nowhere to put such a snippet. Extending Karate means writing a function and calling it through the punctuated path, not adding to the vocabulary. That constraint is the whole bargain. A closed vocabulary is why a Karate feature file needs no companion source file, why a new joiner can read one without opening an IDE, and why the question "which method does this step call?" has no answer worth asking. The cost is that the vocabulary is fixed: when you need something it does not cover, you drop out of the DSL. ## Where teams get this wrong - **Treating the prefix as documentation of dispatch.** Writing `Then` on an HTTP `method` step because it feels like the action does not make it one. The report will say `Then`; the engine will not care. - **Assuming a two-word keyword can be invented.** Only `form`, `multipart`, `soap` and `retry` read past a space. Everything else stops at the first space, so a hoped-for two-word spelling silently becomes a keyword followed by stray words, and fails. - **Reaching for the JavaScript path first.** It is the escape hatch, not the default. A step that could have been `match` or `def` and was written as a bare JavaScript expression loses the built-in's error reporting, which is most of what makes a failure readable.

  • Which words may be followed by a space and still be part of one Karate keyword?
    Only `form`, `multipart`, `soap` and `retry`. Those four are the only first words the parser will read past a space for, and they produce the nine two-word keywords: `form field`/`form fields`, `multipart field`/`fields`/`file`/`files`/`entity`, `soap action` and `retry until`. Every other keyword is exactly one word.
  • Does `* eval karate.log('hi')` differ from `* karate.log('hi')`?
    Not in effect - both evaluate the expression as JavaScript. The first takes the `eval` keyword path explicitly; the second takes the punctuated-token path, because `karate.log(` carries a dot and a parenthesis. `eval` earns its keep when the expression's own first token has no punctuation and would otherwise be read as a keyword.
  • Can a team add a keyword to Karate's vocabulary?
    No. The keyword set is closed and compiled into the engine; there is no registry to add to. The supported way to extend Karate is to define a JavaScript or Java function and call it through the punctuated path, or via `eval`, which is why so much real Karate code has steps whose first token is a dotted or parenthesised expression.

saying these in an interview costs you the question

  • Thinks you register step definitions to add new keywords
  • Claims retry and until are two separate keywords
  • Says every unmatched step silently falls back to JavaScript
  • Believes the eval keyword is required to call a function
  • Assumes a punctuated step is always a syntax error
  • Says the Given prefix routes a step to setup keywords