skip to content

In a Karate feature file, what does the expected value `'#? _ > 0'` assert inside a `match`, and what is `_` bound to while that expression runs?

level: juniorimportance: must knowfreq 58%

answer

  1. The expected side can carry a rule
  2. JavaScript evaluated in place, no step definition
  3. Two names are injected for the expression
  4. Failure message says evaluated to false

basics

~20 s

#? runs the JavaScript that follows it and passes only when the result is true. Karate binds _ to the actual value at that position, so '#? _ > 0' asserts that the value there is greater than zero.

solid answer

~40 s

`#?` is Karate's self-validation marker: everything after the `?` is a JavaScript expression, and the node passes only when that expression evaluates to `true`. Inside it, `_` is the **actual value at that position** — the value of the key the marker is written against, or the whole payload when the marker *is* the entire expected value. `$` is bound alongside it to the **root of the actual side of that same `match` step**, which is how one field gets checked against another. Scenario variables and `def`-ed functions are in scope too, so a long rule can live in a helper and the payload keeps one readable line. A false result fails the step with `evaluated to 'false'`, and the report names the JSON path that failed.

code

gherkin · 5 lines
gherkin
* def date = { month: 3, day: 12 }
* match date == { month: '#? _ > 0 && _ < 13', day: '#number' }

* def temperature = { celsius: 100, fahrenheit: 212 }
* match temperature contains { fahrenheit: '#? _ == $.celsius * 1.8 + 32' }

go deeper

for a junior

Remember the shape: a hash, a question mark, then plain JavaScript, with the underscore standing for the value being checked. Recognising it in someone else's feature file is most of what is asked at this level.

for a middle

Explain that the expression is evaluated on the scenario's own JavaScript engine, that a validator name may precede the question mark and runs first, and that the dollar sign is scoped to the left-hand side of that match step, not the response.

for a senior

Judge when a rule belongs inline and when it belongs in a def-ed function that other features can reuse. Know that a failure names the exact JSON path, and lean on that instead of splitting one match into a dozen single-field assertions.

for a principal

Set the house line on how much logic may live inside a payload. Rules in the expected document keep failures precise, but an expression nobody can unit-test is a liability; a small library of named validator functions is usually the sustainable middle.

## The marker that runs code Karate's `match` compares an **actual** payload against an **expected** one. Most of the expected side is literals and `#`-prefixed type markers, but when no marker states the rule you need, `#?` lets you write the rule itself: the text after the `?` is a JavaScript expression, and its result is the verdict. ```gherkin * def date = { month: 3, day: 12 } * match date == { month: '#? _ > 0 && _ < 13', day: '#number' } ``` Nothing here is a step definition and nothing is a matcher object. The expected value is an ordinary string; Karate sees that it starts with `#`, splits it at the first `?`, evaluates what follows on the scenario's own JavaScript engine, and passes the node only when the result is `true`. ## The two names Karate injects Just before it evaluates the expression, Karate puts two names into the engine, and removes them again as soon as the expression returns: | Name | Bound to | |---|---| | `_` | the **actual value at this position** — "self" | | `$` | the **root of the actual side** of this `match` step | - `_` is whatever sits under the key the marker is written against. In `{ month: '#? _ > 0' }` it is the value of `month`. - A marker used as the *whole* expected value binds `_` to the whole payload, so `match response == '#? _.length == 3'` judges the response itself. - `$` is the top of the left-hand side of **that statement** — not the response, and not the feature file. `match response.data == { ... }` binds `$` to `response.data`, so a field that sits outside `data` is not reachable from the predicate. `$` is what makes a cross-field assertion possible without lifting values into variables first: ```gherkin * def temperature = { celsius: 100, fahrenheit: 212 } * match temperature contains { fahrenheit: '#? _ == $.celsius * 1.8 + 32' } ``` ## Scenario variables are in scope The expression runs on the scenario's engine, so anything `def`-ed is visible to it — bounds, lookup tables, and functions: ```gherkin * def min = 1 * def max = 12 * match date == { month: '#? _ >= min && _ <= max' } * def isValidTime = read('time-validator.js') * match entry == { at: '#? isValidTime(_)' } ``` Pushing anything longer than one line into a `def`-ed function keeps the expected payload readable and makes the rule reusable across features. The function receives the value under test as its argument and returns a boolean. ## Combining a type check with a rule A validator name may sit in front of the `?`, and then **both** checks run — the validator first, the expression only if the validator passed: 1. `'#? _ > 0'` — no type check at all, so JavaScript coercion decides and the string `'3'` passes. 2. `'#number? _ > 0'` — the number validator runs first and rejects anything that is not a number, then the expression runs. 3. `'#string? _.startsWith("SKU-")'` — the same shape for strings. The combined form is usually the one you want: it pins the type and the business rule in a single expected value, and it is one of the reasons Karate never needed a separate schema language. ## What a failure looks like A predicate that returns false fails the step with `evaluated to 'false'`, and the report prints the JSON path of the offending node next to the actual value and the marker string. Because the marker is data inside the payload, that path is exact even for a rule buried several levels down — there is no stack of assertion helpers standing between the failure and the field. ## Return a boolean, not a truthy value Write the comparison out. Karate 1.5.2 requires a genuine boolean: its internal truth test accepts only a `Boolean`, so `'#? _.length'` fails against a three-element array even though JavaScript would call that value truthy. Karate 2.x evaluates full JavaScript truthiness and the same expression passes. `'#? _.length == 3'` says what you mean and is correct on both lines, so make the comparison explicit and the question never arises. ## Where it fits Reach for `#?` when the expectation is a **rule** rather than a value or a type — a range, a relationship between two fields, a format the built-in validators do not cover, a membership test against a list you loaded. Reach for a plain literal when the value is known and fixed, and for a type marker when only the shape matters. Mixing all three inside one expected payload is the point of the design: one step, one failure report, and no assertion code to maintain beside the test.

  • Does a `#?` predicate that returns a truthy non-boolean, such as `'#? _.length'`, pass?
    Not on both lines. Karate 1.5.2 demands a real boolean — its truth check accepts only a `Boolean`, so a number result fails with `evaluated to 'false'`. Karate 2.x applies full JavaScript truthiness and the same expression passes. Write the comparison out, `'#? _.length == 3'`, and the assertion is correct either way.
  • How do you keep a rule that is longer than one line out of the expected payload?
    `def` a JavaScript function — inline, or with `read('validator.js')` — and call it from the marker: `{ at: '#? isValidTime(_)' }`. The function receives the value under test as its argument and returns a boolean. The payload keeps one readable line per field, and the logic sits somewhere it can be reused by other features.
  • What is `$` bound to when the left-hand side of the `match` is not the whole response?
    The root of that statement's actual side. `match response.data == { id: '#? $.tenant != null' }` binds `$` to `response.data`, not to `response`, so a field outside that subtree is unreachable. Widen the left-hand side — `match response contains { data: { ... } }` — or `def` the value into a variable and reference it by name instead.

saying these in an interview costs you the question

  • Thinks the marker needs a step definition or a custom matcher class
  • Says the underscore is the whole response rather than the value at that key
  • Believes the dollar sign is always the response, whatever the match compares
  • Assumes any truthy value passes, instead of writing an explicit comparison
  • Confuses it with the round-bracket form, which computes a value rather than a verdict
  • Claims Karate needs a JSON Schema document to express a rule like this