Articles
Karate API Testing Interview Questions: Match Semantics, Reusable Flows, and Parallel Isolation
Practice Karate API testing interview questions with exact versus subset match contracts, reusable feature scope, and parallel tenant isolation.
Karate API testing interviews often turn on three deceptively small choices: what a match assertion permits, whether a called feature shares or isolates variables, and whether two scenarios can safely run at the same time. A green suite can still accept a missing field, reuse one user's token for another, or depend on execution order. The best answer turns each choice into an explicit contract and a reproducible trace.
This article uses original API examples, not a claim about any employer's test process. Karate's syntax reference, match documentation, calling-features guide, and parallel-execution guide establish the framework behavior. The question in an interview is what the test actually proves.
For a practical endpoint to reason about, use Implement CRUD API and tests and write down the response fields that each assertion must protect.
Interview question: what does this match allow?
Suppose a GET /orders/o7 response should include an ID, status, and total. The test author writes * match response contains { id: 'o7' }. That checks the presence of the ID while deliberately allowing other keys. It does not prove status, total, or absence of unexpected fields. By contrast, match response == expected expresses a strict equality contract for that object. Karate's match guide documents exact, subset, and other match forms. State which contract the API requires before choosing one.
A practical answer uses a small matrix:
| Assertion | Intended contract | Bug it can miss |
|---|---|---|
match response == expected | Exact expected structure and values | It may be too rigid for deliberately variable fields unless the expectation uses the proper markers |
match response contains { id: 'o7' } | At least this key and value | Missing business fields or unexpected extras |
match response.items contains only [...] | The array has exactly the expected members, without relying on order | It may ignore ordering when order is the API contract |
match response contains deep {...} | Required nested values occur inside a larger structure | Unexpected nested fields remain allowed |
The exact semantics of array matching deserve attention. contains only expresses membership without extra elements; it is not the assertion for a sorted ranking. If the API promises newest-first, compare the sequence or assert ordering separately. If duplicates matter, construct an example with repeated values and verify the operator's behavior in the Karate version you run. Do not silently equate “contains the right IDs” with “returns exactly once in the right order.”
Karate supports schema and predicate markers such as type checks and optional fields. Use them where the contract truly allows variation. For a server-generated timestamp, checking its type may be appropriate, while copying one fixed timestamp into an exact expected object is brittle. An optional field marker should reflect an optional API field, not hide a regression that dropped a required one.
Consider a failed load that returns HTTP 200 with { id: 'o7', status: 'paid' } but omits total. The subset ID assertion passes. An exact expected object or a targeted required-field assertion fails. Before an interview, practice explaining which of these outcomes is correct for the product requirement. The framework cannot infer the intended schema from a convenient assertion operator.
How do reusable features pass state?
Karate features can call another feature to reuse setup or a flow. The calling-features guide distinguishes passing an argument and assigning a call result from invoking a feature in shared scope. That distinction determines whether a called feature's variables stay inside a returned object or appear in the caller's context.
A safe mental model is an explicit login function. Give it a tenant and credentials; let it return a JSON result containing a token and user ID; bind that result to a local variable in the scenario. This makes the dependency visible:
* def login = call read('classpath:auth/login.feature') { tenant: 'tenant-A', user: 'alice' }
* def token = login.token
Given path 'orders', 'o7'
And header Authorization = 'Bearer ' + token
When method get
Then status 200
And match response contains { id: 'o7' }
The snippet is schematic; check the actual feature and argument syntax against the version and project conventions. The contract is that this scenario uses Alice's token for tenant A. A shared-scope call can be useful when you intentionally want a called feature to define variables in the caller, but it makes state flow less obvious. In a code review, identify every variable introduced or overwritten by a reusable feature. Names such as token or baseUrl are especially prone to accidental collision.
The feature-file syntax guide explains that a Background runs before each scenario. That makes it a good place for per-scenario setup, provided the setup is safe and affordable. Do not treat a Background as a once-per-feature cache. If setup creates a database row on every scenario, those rows need distinct IDs or a cleanup policy; otherwise, parallel execution can turn a supposedly shared fixture into collisions.
Karate's callonce and karate.callSingle() serve different reuse scopes. Per the calling-features guide, callonce caches for a feature and karate.callSingle() can initialize once across a suite. The latter is suitable for stable setup data that can be shared across parallel scenarios when the returned result is a simple, serializable data object. A mutable response object or one user's short-lived token shared across tenants is a poor global fixture. Cache scope is a correctness decision, not merely a speed optimization.
Ask what happens when two scenarios start simultaneously. If both rely on the same cached token, is the token valid for both users? If a setup feature creates one order ID, do both tests mutate that order? If a test modifies a global Java object, can another test read it mid-update? Make the owner of each piece of state explicit: local scenario data, per-feature setup, suite-wide immutable configuration, or server-side fixture.
Interview question: why does the suite fail only in parallel?
Karate is designed for parallel scenario execution. Its parallel-execution documentation emphasizes independent scenarios and version-specific controls. If the suite passes with one worker but fails with several, suspect shared server records, reused identifiers, setup races, unisolated environment state, or assumptions about scenario order before blaming the assertion engine.
Use an original two-tenant trace. Scenario A creates order A-1001 under tenant A, while scenario B creates B-2007 under tenant B. Each scenario uses its own token and unique ID. A failing test replaces both IDs with test-order and uses a globally cached token from the first scenario. Either scenario may overwrite the same row, and one request may carry the wrong tenant's authorization. The visual shows the separation that the test needs.
To diagnose, log the scenario name, worker, tenant, token subject if safe to expose in local logs, and resource ID. Do not log raw credentials or access tokens. Inspect the first divergence: did creation return the wrong owner, did the API reject an authorization header, or did a later cleanup delete a record another scenario was asserting? A failure at the final match line may be the symptom of a race during setup.
If a server contract truly demands serial execution, use the control supported by the installed Karate version and document why that shared resource cannot be isolated. Current Karate documentation includes version-sensitive tags such as @lock; older @parallel=false behavior is not a safe assumption for a newer runner. Prefer redesigning test data to be independent when possible, because a blanket serialization tag can hide production concurrency bugs and slow the suite. Where ordering is part of one workflow, keep the steps in a single scenario or called flow rather than relying on scenario execution order.
Distinguish parallel test execution from server transaction isolation. Karate can launch requests concurrently; the server still decides what happens when they touch the same data. A clean test suite uses unique records and checks the intended server behavior. If the interview asks for a race test, shared data is intentional, but then the expected outcomes must be stated: for example, exactly one of two competing creates succeeds under a uniqueness constraint. A flaky race test with an unspecified winner teaches little.
How do you design a recoverable API test flow?
A robust flow tests one observable contract at a time. Create an order with a unique idempotency key, assert the response status and identifying fields, read it back, update an allowed field, and verify the final state. If the API owns eventual consistency, poll a bounded observable condition rather than sleep a fixed interval. If a step fails, log the request method, sanitized path, status, correlation ID, and the matched response fragment. That gives a reviewer enough to distinguish setup failure from product failure.
Reusable setup is helpful when it removes duplication without concealing the operation under test. If every scenario calls a feature that performs login, creates an order, updates it, and catches all errors, a failure in the setup can make the main scenario meaningless. Return a small result object and assert setup preconditions at the boundary. Keep the test's main business action visible.
For a negative-path test, choose what must remain unchanged after the rejected request. An unauthorized update should fail with the expected status and leave the order's owner and total intact. A weak test checks only the error code; a stronger one reads back the record. That is an inference about good test design from the API contract, not a guarantee offered by Karate itself.
If the endpoint emits volatile fields, a strict full-response match might be the wrong expression. You can combine targeted type markers for volatile fields with strict assertions for stable identifiers and totals. If extra fields are forbidden by a versioned public schema, assert that explicitly. Explain the trade-off: permissive matches reduce brittle failures but can hide a schema regression; exact matches catch drift but need maintenance when intended fields change.
What should you rehearse for a Karate interview?
| Question | Karate practice angle | Follow-up to answer aloud |
|---|---|---|
| Implement CRUD API and tests | Response contract | Which fields need exact versus type assertions? |
| Debug and Harden a Ticketing Backend API | Failure trace | Which setup step first produced the wrong state? |
| Validate Unit-Test Coverage and Identify Missing Scenarios | Coverage gaps | Which negative path needs a read-back assertion? |
| Design a Test Orchestration System | Parallel isolation | What resources may workers share safely? |
| Explain HTTP methods and your usage | Method semantics | What state should remain after a rejected write? |
A strong spoken answer is: “I start from the API contract, then select ==, contains, contains only, or a deep/schema match for exactly what the contract allows. I make called-feature inputs and outputs explicit, choose the right cache scope, and give each parallel scenario distinct credentials and resource IDs. When a parallel failure appears, I trace the first shared state or setup collision before loosening the assertion.” If the interviewer changes the API from unordered results to sorted results, or from immutable setup to a changing user token, change the assertion and cache design accordingly.
For practice, take Implement CRUD API and tests. Draft one exact response assertion, one allowed-subset assertion, and one parallel fixture plan. Explain the bug each assertion catches and the bug it intentionally does not catch.