skip to content

Reuse and Defaults

Where a feature file gets values it never wrote itself: another feature it calls, or JavaScript that ran before it. Interviewers probe it because this is where a suite hides its setup.

on this pageshow

explore

questions

21

In a Karate feature file, a `Background` runs before every scenario — so how does `* def token = callonce read('auth-token.feature')` avoid re-running that feature for each one?

level: juniorimportance: must knowfreq 70%

answer

  1. One keyword differs by one letter
  2. The Background is not run once
  3. Result is remembered, callee is not re-run
  4. Cache belongs to the running feature

basics

~20 s

callonce runs the called feature only the first time and caches what it returned, so every later scenario in that feature file reuses it. Plain call re-executes the login for every scenario and every Examples row.

solid answer

~40 s

`call` and `callonce` take the same expression and behave identically apart from one thing: `callonce` remembers the result. The first scenario that reaches the step executes `login.feature`; every later scenario in the **same feature file** gets a copy of that cached result without a second HTTP round trip. That matters because Karate re-runs the whole `Background` before every `Scenario` and before every `Examples` row, so a `call` there is a sign-in per row, not per file. The cache is held on the running feature, so a second feature file that runs the same `callonce` line executes `login.feature` again. It is also keyed on the call expression itself, and it is thread-safe: parallel scenarios of that feature contend on a lock and only one of them actually runs the callee.

code

gherkin · 22 lines
gherkin
Feature: cats

Background:
  * url baseUrl
  # runs once for this whole file, not once per scenario
  * def auth = callonce read('login.feature')
  * header Authorization = auth.token

Scenario: list cats
  Given path 'cats'
  When method get
  Then status 200

Scenario Outline: fetch <id>
  Given path 'cats', <id>
  When method get
  Then status 200

  Examples:
    | id |
    | 1  |
    | 2  |

go deeper

for a junior

Recall that callonce is call plus a cache, and that the Background it usually sits in re-runs for every scenario and every Examples row.

for a middle

Be able to say where the cache lives (the running feature), what keys it (the call expression), and that a failed call is not cached.

for a senior

Judge which Background work is worth caching: expensive and idempotent yes, anything a scenario must see fresh no. Watch for functions smuggled through the result.

for a principal

Weigh per-file caching against a suite-wide one: callonce keeps each feature independently runnable, at the cost of one sign-in per file.

## The one difference `call` and `callonce` are two step keywords that accept exactly the same expression — a feature file read with `read(...)`, a JavaScript function, a variable holding either — and hand it the same optional argument. Everything about how the callee runs, what it can see and what it hands back is shared. The single difference is that `callonce` consults a cache first, and on a miss stores what the call produced. | | `call` | `callonce` | |---|---|---| | Executes the callee | every time the step runs | first time only | | Later runs of the step | full execution again | copy of the cached result | | Cache lifetime | none | the running feature | | Cache key | none | the call expression as written | ## Why the `Background` is where this bites Karate re-evaluates the `Background` before **every** `Scenario`, and before **every row** of an `Examples` table. That is the reason a cache exists at all: a feature file with three scenarios and a five-row outline runs its `Background` eight times, so a `call read('login.feature')` there is eight sign-ins. Swapping in `callonce` collapses those eight to one while leaving the rest of the `Background` — the `url`, the headers, the seed data — running per scenario as it should. The idiom that follows is the one you will see in real suites: - put cheap, scenario-local setup in the `Background` with plain steps; - put the expensive, idempotent part behind `callonce`; - keep anything that must be fresh per scenario **out** of the cached callee. ## What comes back on a cache hit On a hit the scenario is handed a copy of the cached result, not the cached object itself, so ordinary use does not hand the next scenario a mutated value. What is cached is *data* — the variables the callee produced. Karate's own documentation is blunt that a `callonce` result should ideally be pure JSON: the mechanism exists to cache **data, not behaviour**. A JavaScript function returned from a cached call still resolves its variables against the scope it was created in, which is the callee's context at the moment of the one real execution, not the scope of whichever scenario later receives it. ## Concurrency and failure Two details are worth knowing because they are easy to guess wrong: 1. **Parallel scenarios are handled.** The cache is guarded by a lock with a double-check, so when several scenarios of the same feature start at once exactly one executes the callee and the rest wait and take the cached result. You do not need to serialise anything yourself. 2. **A failed call is not cached.** If the callee throws, nothing is stored, and the next scenario in the feature tries again. This is the opposite of `karate.callSingle()`, which deliberately caches the exception so every later caller fails immediately with the same error. ## Where the boundary of the cache is The cache belongs to the feature that is running, not to the file being called. Concretely: - All scenarios and all `Examples` rows of one top-level feature share one cache, so the callee runs once for that file. - A second feature file containing the identical `callonce` line has its own cache and runs the callee again. Ten feature files means ten sign-ins. - A feature that is itself *called* gets a fresh cache on each invocation, so a `callonce` inside a called feature does not de-duplicate across the calls that invoke it. That last boundary is the practical reason `callonce` alone does not solve a suite-wide sign-in. When you want the login to happen once for the whole run regardless of how many feature files there are, the tool is `karate.callSingle()` from `karate-config.js`, whose cache is held for the suite instead of the feature. `callonce` is the right answer when the expensive thing is genuinely per-file — seeding records this feature will assert on, fetching a reference list this feature needs — and when you would rather each file be independently runnable. ## Recognising it in review A `call` in a `Background` is not automatically wrong; it is wrong when the callee is expensive or non-idempotent. Reading a small JSON fixture per scenario costs nothing. Creating three kittens per scenario, or exchanging credentials for a token per scenario, is the shape that wants `callonce` — and the symptom in a failing suite is usually the callee's own side effects piling up, not the wall-clock time.

  • In Karate, does `callonce` de-duplicate the call across two different feature files?
    No. The cache is held by the feature that is executing, so each top-level feature file has its own. Two files carrying the same `callonce read('login.feature')` line each execute `login.feature` once, giving two sign-ins. To collapse a login to one per run across every file, call it with `karate.callSingle()` from `karate-config.js`, whose cache is suite-wide.
  • In Karate, what happens on the next scenario if the feature behind a `callonce` fails?
    Nothing is written to the cache when the callee throws, so the failure propagates to the current scenario and the next scenario re-executes the call. That is worth contrasting with `karate.callSingle()`, which caches the exception itself and re-throws it to every later caller so the expensive failing step is attempted only once.
  • In Karate, is `callonce` safe when the feature's scenarios run in parallel?
    Yes. The lookup is guarded by a lock with a re-check after acquiring it, so when several scenarios of the feature hit the step simultaneously exactly one executes the callee and the others block briefly and then take the cached result. Karate logs the wait when a thread had to queue.

Like memoizing a function: same call, same arguments, and after the first evaluation the answer is handed back from a note rather than recomputed.

saying these in an interview costs you the question

  • Says callonce and call differ in what the callee can see
  • Thinks the Background already runs only once per feature
  • Believes callonce is global across the whole run
  • Expects a failed callonce to be cached and skipped afterwards
  • Returns JS functions or Java objects from a callonce result
  • Thinks Examples rows skip the Background
open as a page

In a Karate feature suite, what must the `karate-config.js` file contain, and what does Karate do with the value it produces?

level: juniorimportance: must knowfreq 78%

basics

~20 s

karate-config.js holds a JavaScript function that returns a map. Karate evaluates it before every top-level scenario and applies each key of that map as a scenario variable, so no step-definition or glue class is involved.

open as a page

In a Karate feature file, what is the difference between the step `* configure headers = { 'X-Trace': 'abc' }` and the step `* header X-Trace = 'abc'`?

level: juniorimportance: must knowfreq 70%

basics

~20 s

configure headers sets a default header map that Karate re-applies to every later request in scope. The header step sets one header on the request being built, and Karate clears it as soon as that call completes.

open as a page

In a Karate feature file, what is the difference between `* call read('login.feature')` and `* def auth = call read('login.feature')`?

level: juniorimportance: must knowfreq 70%

basics

~20 s

The def assignment decides scope. A bare call read('login.feature') merges every variable the called feature defined into the caller; def auth = call read('login.feature') isolates the call and hands those variables back as a map instead.

open as a page

In Karate, how do `callonce` and `karate.callSingle()` differ in how wide their cache is and in what each one uses as the cache key?

level: middleimportance: must knowfreq 62%

basics

~20 s

callonce caches per feature and keys on the call expression exactly as written. karate.callSingle() caches per suite, so once for the whole run across every feature, and keys on the path string you pass it.

open as a page

In Karate, in what order are `karate-base.js`, `karate-config.js` and `karate-config-<env>.js` evaluated, and what happens when two of them return the same key?

level: middleimportance: must knowfreq 62%

basics

~10 s

Karate evaluates karate-base.js first, then karate-config.js, then karate-config-<env>.js, applying each returned map in turn. A later file wins on a shared key, and it replaces that whole top-level value rather than merging into it.

open as a page

How do you point one Karate suite at a different environment, and what does `karate.env` hold inside `karate-config.js` when nothing has set it?

level: middleimportance: must knowfreq 66%

basics

~10 s

Set the karate.env system property, for example -Dkarate.env=qa, or call karateEnv("qa") on the runner builder. Karate then also looks for karate-config-qa.js and exposes the name as karate.env, which is null when nothing set it.

open as a page

In a Karate feature file, how far does a `configure headers` step reach when it sits in the `Background` versus inside one `Scenario`, and what does `* configure headers = null` do?

level: middleimportance: must knowfreq 62%

basics

~20 s

A Background configure re-runs before every scenario, so each one starts from the same state. A configure inside a scenario reaches only that scenario. Assigning null clears that one key for the rest of it.

open as a page

In Karate, when you write `* def result = call read('helper.feature') { id: 5 }`, what exactly does `result` hold?

level: middleimportance: must knowfreq 52%

basics

~10 s

A map of the variables the called feature contributed itself, taken from the last scenario it executed. Caller variables it merely inherited to read are excluded, and the call argument's keys are included.

open as a page

In a Karate feature file, what does `karate.setupOnce()` run, and how is it different from a `callonce` of another feature?

level: middleimportance: should knowfreq 30%

basics

~20 s

karate.setupOnce() runs the Scenario tagged @setup in the same feature file and caches its variables for that feature. Unlike callonce, the target is a scenario in this file rather than another feature, and it is skipped by any ordinary run.

open as a page

In a Karate feature file, what does an `@env=dev` tag on a scenario do, and what happens to that scenario when `karate.env` is not set at all?

level: middleimportance: should knowfreq 47%

basics

~20 s

@env=dev runs that scenario only when karate.env is dev; a comma-separated list accepts any of the names. If karate.env is not set, the scenario is skipped. Its mirror, @envnot=dev, skips when karate.env is dev and runs when it is unset.

open as a page

In Karate, what does `karate.configure('readTimeout', 5000)` do, and when would you write that instead of a `* configure readTimeout = 5000` step?

level: middleimportance: should knowfreq 52%

basics

~10 s

It does exactly what the configure step does; both hand the same key and value to the same parser. The JavaScript form is for places with no Gherkin step to write, above all karate-config.js.

open as a page

In a Karate feature file, what does `* configure retry = { count: 10, interval: 500 }` change, and what happens if you write `* configure retry = 10` instead?

level: middleimportance: should knowfreq 46%

basics

~10 s

It changes the defaults a retry until step polls with: attempts and milliseconds between them, replacing the built-in 3 and 3000. A bare number is silently ignored, because the value must be a map.

open as a page

In a Karate feature file, what does `* configure ssl = true` do, and does it make Karate verify the server's TLS certificate?

level: middleimportance: should knowfreq 44%

basics

~20 s

It switches the HTTP client onto Karate's own SSL context, which trusts every certificate. It is the opposite of verification: trustAll defaults to true, which is exactly why a self-signed or expired certificate stops being a problem.

open as a page

In Karate, how does `* karate.call('login.feature')` differ from the step `* call read('login.feature')`, and how do you get shared scope from the JavaScript form?

level: middleimportance: should knowfreq 38%

basics

~10 s

They default to opposite scopes. The bare call step shares scope; karate.call(path) is isolated and simply returns the result map. Pass a boolean first argument, karate.call(true, path), to share scope from JavaScript.

open as a page

In Karate, what does `configure callSingleCache = { minutes: 15 }` change about `karate.callSingle()`, and what is the default?

level: seniorimportance: should knowfreq 34%

basics

~20 s

It spills the callSingle result to a file under the build directory and reuses it for fifteen minutes, so repeated local runs skip the sign-in entirely. The default is minutes 0, meaning memory only and one execution per run.

open as a page

Two scenarios in one Karate feature each need a different login, and both go through `* def token = callonce read('login.feature') creds` where `creds` is set per scenario. Both end up with the first scenario's token — why, and how do you fix it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

The callonce cache is keyed on the expression text, not on the argument's value. Both steps carry identical text, so they share one entry and the second scenario gets the first one's token. Fix it by making the expressions differ.

open as a page

A reusable Karate feature picks up the right `baseUrl` when run on its own, but the wrong one when the same file is reached through `call read('setup.feature')` from another suite. What is the mechanism?

level: seniorimportance: should knowfreq 44%

basics

~10 s

Karate evaluates the karate-config.js chain only for top-level scenarios. A feature reached through call never re-runs it; it inherits the caller's variables and configure settings instead, so it sees whatever the caller's config produced.

open as a page

In Karate, what does `* configure afterScenario = function(){ ... }` do, and why does the same line have no effect inside a feature that is invoked with `call`?

level: seniorimportance: should knowfreq 42%

basics

~20 s

It assigns a JavaScript function to a config key, and Karate runs it after every scenario in scope, on the passing and the failing path. Hooks fire for top-level scenarios only, so a called feature's never runs.

open as a page

A reusable Karate feature ends with `* configure headers = read('headers.js')`. Called as `* def auth = call read('auth.feature')`, the caller's next request still goes out without those headers. Why, and what are the two ways to fix it?

level: seniorimportance: should knowfreq 42%

basics

~10 s

The def made the call isolated, and configuration travels downward into a callee but never back up. Either drop the def so the call shares scope, or repeat the configure step in the caller.

open as a page

Inside a Karate feature invoked with `* call read('helper.feature') { id: 5 }`, what do the `__arg` and `__loop` variables hold?

level: middleimportance: nice to knowfreq 30%

basics

~20 s

__arg holds the whole call argument, here the map { id: 5 }, or null when none was passed. __loop holds the zero-based iteration index when the feature is called once per array element, and -1 otherwise.

open as a page