In a Karate feature file, what forms can the left-hand side of a `match` step take, and what does a bare `$` refer to there?
answer
- Four accepted shapes, not one
- Leading identifier is the variable
- One character means the response
- Parentheses switch to JavaScript
- Dollar prefix optional on the left
basics
~20 sThe left side of a Karate match step is a variable name, a JsonPath or XPath rooted at a variable such as response.name, a function or method call, or parenthesised JavaScript. A bare $ stands for the response, and a left side that starts with / is a bare XPath rooted there too.
solid answer
~50 s`match` is a built-in step, so the whole assertion lives in the feature file with no step definition behind it. Karate accepts four shapes on the left: a plain variable name (`match cat == {...}`), a *named* JsonPath or XPath rooted at a variable (`match response.name == 'Billie'`, `match cat.kittens[*].id == [23, 42]`), a function or method call (`match foo.bar() == 3`), or anything wrapped in parentheses, which is evaluated as JavaScript (`match (foo + bar) == 'ab'`). A left side that is exactly `$`, or that starts `$.` or `$[`, is rooted at the `response` variable, so `match $.name == 'Billie'` and `match response.name == 'Billie'` do the same thing. A left side that starts with `/` is a bare XPath and is rooted at `response` the same way. You never need a `$` in front of a variable name on the left — the JsonPath is implied there, and the no-dollar form is the documented style.
code
gherkin · 30 lines* def cat =
"""
{
name: 'Billie',
kittens: [
{ id: 23, name: 'Bob' },
{ id: 42, name: 'Wild' }
]
}
"""
# 1. a variable name - compares the whole value
* def expected = { name: 'Billie', kittens: [{ id: 23, name: 'Bob' }, { id: 42, name: 'Wild' }] }
* match cat == expected
# 2. a named JsonPath rooted at a variable
* match cat.name == 'Billie'
* match cat.kittens[0].name == 'Bob'
# the two-token form: variable, then a full JsonPath
* match cat $.kittens[*].name == ['Bob', 'Wild']
# 3. a function or method call
* def firstId = function(c){ return c.kittens[0].id }
* match firstId(cat) == 23
# 4. parentheses - evaluated as JavaScript
* def a = 1
* def b = 2
* match (a + b) == 3go deeper
Remember the everyday shape: the variable name, then the path into it, then the comparison. Learn that a lone $ means the last response so you can read other people's feature files.
Explain the split: Karate takes the leading identifier as the variable and hands the rest to the JsonPath engine as $ plus the remainder. Know the two-token form and the parentheses escape hatch.
Steer a team toward the documented style — no dollar on the left, parentheses only where a path genuinely cannot express the check — so assertions stay greppable and reviewers can see what is being selected.
The reason there are four shapes is that the assertion language has no compile step: the shape is settled at run time, mostly from the step text but partly from what the leading variable holds. Weigh that flexibility against the reviewability of a suite where any line can quietly become JavaScript.
## The left side is part of the feature file Karate has **no step definitions**. There is no glue class to hop into, no regex registry, no `@Given` method that receives a captured string. `match` is built into the runtime, which means the thing you are selecting and the value you expect both sit in the `.feature` text, and the runtime has to work out what you meant from that text — and, for a dotted left side, from what the leading variable turns out to hold. Karate's documentation lists exactly four shapes the left-hand side may take: 1. **A variable name** — `match cat == { name: 'Billie' }` compares the whole variable. 2. **A "named" JsonPath or XPath rooted at a variable** — `match response.name == 'Billie'`, `match cat.kittens[*].id == [23, 42]`, `match response..username contains 'Bret'`. 3. **A function or method call** — `match foo.bar() == 3` or `match foo.bar('hello').baz == 1`. 4. **Anything wrapped in parentheses**, evaluated as JavaScript — `match (foo + bar) == 'ab'`, `match (1 == 2) == false`. Shape 2 is the one you write all day, and it is why the left side reads like a path rather than an expression. Two more leading characters widen that list in practice. A left side beginning with `/` is a **bare XPath**, rooted at `response` exactly the way a bare `$` is — `match /cat/name == 'Billie'`. A left side beginning with `[` or `{` is a **JSON literal**: Karate wraps it in parentheses for you and hands it to the JavaScript engine, which is shape 4 under another spelling. So "the left side starts with the variable name or with `$`" is a rule about a *path* on the left, not about the left side as a whole. ## How a path is split For a named JsonPath, Karate takes the leading identifier as the **variable** and everything after it as the **path**, prefixed with `$`. So: | You write | Variable | JsonPath applied | |---|---|---| | `response.name` | `response` | `$.name` | | `cat.kittens[*].id` | `cat` | `$.kittens[*].id` | | `foo[0].bar` | `foo` | `$[0].bar` | | `cat` | `cat` | `$` (the whole value) | There is also a two-token form where you name the variable and then give a full JsonPath, separated by a space: `match cat $.kittens[*].name == ['Bob', 'Wild']`. That is useful when you want to write pure JsonPath, or when the path starts with something the single-token split would not handle. ## `$` is the response A left side that is exactly `$`, or begins `$.` or `$[`, is rooted at the `response` variable — the one Karate auto-binds after every HTTP call, alongside `responseStatus`, `responseHeaders` and `responseTime`. These pairs are equivalent: ```gherkin * match $ == { name: 'Billie' } * match response == { name: 'Billie' } * match $.name == 'Billie' * match response.name == 'Billie' ``` The `$` form is a convenience for the common case of asserting straight against the last response. Once you are selecting out of some other variable, name it: `match cat.name == 'Billie'`. ## You do not need `$` before a variable name On the **left** of `match`, the JsonPath context is implied, so `match $cat.name == 'Billie'` and `match cat.name == 'Billie'` resolve the same way. Karate accepts the leading `$` there but it buys you nothing, and the documentation is explicit that none of its own examples use it on the left — the no-dollar form is the house style. The `$variable` form earns its keep somewhere else: on the **right**-hand side and in other expression positions, where a bare `data[*].a` is not a path context. `match foo[*].a == $data[*].a` is the idiomatic way to compare two selections. ## When it is not a path at all Because the left side can also be JavaScript, there is an escape hatch for anything the path split cannot express. Two rules keep you out of trouble: - If the left side involves an operator, a computed value, or a variable you want interpolated, **wrap it in parentheses** — inside them it is plain JavaScript and ordinary variables are in scope. - If the left side is a method call, write it as one; `match foo.bar() == 3` is a supported shape and does not need parentheses. ## Why interviewers ask this It separates a candidate who has written Karate from one who has read about it. The tell-tale wrong answers are "you put a matcher object from a Java assertion library on the left" (there are none — the expected value goes on the right as literal JSON) and "you need a step definition to expose `response`" (you do not — it is auto-bound). Being able to say *variable, named JsonPath, method call, or parenthesised JavaScript*, and that `$` is the response, is the whole answer. The follow-up usually probes the boundary. Karate picks the shape mostly from the **text of the step** — a trailing `()` is a call, a leading `(` is JavaScript — but the routing is not purely textual. For a dotted left side Karate evaluates the leading identifier *first* and then re-routes on what came back: if the value is neither a map nor a list it throws the variable-plus-path split away and evaluates the whole left side as JavaScript instead, the `driver.cookies` edge case named in Karate's own source. So `match foo.length == 3` applies the JsonPath `$.length` when `foo` holds JSON, and is a plain JavaScript property read when `foo` holds a string: the same characters, a different route, decided by the runtime type. It is also why the right-hand side is a different world: there the expected value is written as literal JSON or XML, with embedded expressions and fuzzy markers, and no path context is implied. Keeping the two sides straight — *select on the left, describe on the right* — is what makes a Karate assertion readable in one pass.
- Where does the `$variable` short-cut actually matter, if it is optional on the left of `match`?On the right-hand side and in other expression positions. On the left, the JsonPath context is implied and Karate resolves `$cat.name` and `cat.name` identically. On the right there is no implied path context, so `match foo[*].a == $data[*].a` is the documented way to say "apply this JsonPath to the variable `data`" — as is the equivalent `get data[*].a`.
- If the left side can also be JavaScript, when do you need the parentheses?When the expression is not a path or a call — an operator, a literal, a comparison, or anything that needs a variable interpolated. `match (a + b) == 3` and `match (1 == 2) == false` both need them. A plain path (`cat.name`) or a method call (`cat.name.toUpperCase()`) does not, and on the right, parenthesising a JSON literal forces it through the JavaScript engine rather than the lenient JSON parser — on the left Karate adds those parentheses for you.
- What is bound for you after an HTTP call, besides `response`?`responseStatus` (the status code as an integer), `responseHeaders` (a map of lists), `responseTime` (milliseconds), `responseCookies` and `responseBytes`. You normally assert the code with the `status` keyword rather than reading `responseStatus`, but all of them are ordinary variables you can put on the left of a `match`.
saying these in an interview costs you the question
- Says a Java matcher object belongs on the left
- Thinks a step definition is needed to expose the response
- Claims the leading dollar sign is mandatory on the left
- Believes only a plain variable name is allowed there
- Thinks the left side is always JavaScript, never a path
- Confuses the dollar sign with a doc-string or placeholder