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?
answer
- Two axes: how wide, and keyed on what
- One is a keyword, one is JS only
- Feature versus suite
- Expression text versus path string
- The path key is why ?suffix exists
basics
~20 scallonce 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.
solid answer
~50 sThey differ on two axes, width and key. **`callonce`** is a step keyword whose cache belongs to the feature that is running: all its scenarios and `Examples` rows share it, a second feature file gets its own, and the key is the call expression text after the keyword. **`karate.callSingle()`** is a JavaScript API with no step-keyword form; its cache belongs to the suite, so the target runs once for the entire run no matter how many feature files call it, and the key is the path string argument. That is why `callSingle` is normally invoked from `karate-config.js` — the config is re-evaluated for every scenario, so an uncached sign-in there would run per scenario. Because the key is the path, calling the same file with two different arguments needs a `?suffix` on the path to de-duplicate. A third helper, `karate.setupOnce()`, is feature-wide like `callonce` but targets a `@setup` scenario in the same file.
code
javascript · 8 linesfunction fn() {
var config = { baseUrl: 'https://api.example.com' };
// karate-config.js is re-evaluated for every scenario,
// so this exchange must be cached or it runs per scenario
var auth = karate.callSingle('classpath:get-token.feature', config);
config.authToken = auth.token;
return config;
}go deeper
Learn the two names and the one-line difference: callonce is per feature file, karate.callSingle() is once for the whole run.
Explain both cache keys out loud — expression text for callonce, path string for callSingle — and why the config file is where callSingle belongs.
Know the failure asymmetry and the ?suffix de-duplication, and spot the case where an argument that varies collides on one key.
Decide how wide a cached sign-in should be for the suite as a whole, trading one global token against feature files that no longer run standalone.
## Three caches, not one Karate ships three separate once-only mechanisms, and interviewers ask which you reach for because they differ on width and on key. | Mechanism | Form | Cache width | Cache key | |---|---|---|---| | `callonce` | step keyword | the running feature | the call expression as written | | `karate.callSingle()` | JS API only | the suite (one run) | the path string argument | | `karate.setupOnce()` | JS API only | the running feature | the `@setup` scenario name | Only `callonce` has a step-keyword spelling. There is no `callsingle` or `setuponce` keyword — both are functions on the `karate` object, which is why you see them inside `karate-config.js` and inside `#(...)`-style expressions rather than at the start of a step. ## Width: what "once" is measured against `callonce`'s cache is held by the feature that is executing. Every scenario and every `Examples` row of that file shares it, so the callee runs once per file. Ten feature files each carrying the same `callonce` line means ten executions. A feature that is itself being *called* gets a fresh cache per invocation, so a `callonce` buried inside a called feature does not de-duplicate across the calls that invoke it. `karate.callSingle()`'s cache is held by the suite — the object representing one run. It therefore runs once across every feature in that run, in parallel or not, which is what "single" means. Run the runner twice in the same JVM and you get two suites and two executions. That difference is the whole reason `callSingle` exists. `karate-config.js` is re-evaluated for **every** top-level scenario, so a plain sign-in written there would fire per scenario. Wrapping it in `karate.callSingle()` collapses that to one exchange for the run while leaving the rest of the config free to compute per-scenario values. ## Key: expression text versus path string The keys are where candidates get it wrong. - `callonce` keys on the **call expression text** — everything after the keyword. `callonce read('a.feature')` and `callonce read('b.feature')` are two entries. Two steps whose text is byte-identical are one entry even if the variables they reference now hold different values. - `karate.callSingle()` keys on the **path string you passed**, not on the file it resolves to and not on the argument. Two calls to the same file with different arguments collide on one entry. Because the argument is not part of the `callSingle` key, Karate supports a `?name` suffix on the path purely to split the key. The suffix is stripped before the file is read, so both calls run the same file and land in different cache slots: ```javascript var admin = karate.callSingle('classpath:get-token.feature?admin', { username: 'admin' }); var user = karate.callSingle('classpath:get-token.feature?user', { username: 'user' }); ``` ## Failure, and why it differs The two caches also disagree about failure, and the asymmetry is deliberate: 1. **`callonce` does not cache a failure.** The store happens after the call returns, so a throw leaves the slot empty and the next scenario in the feature tries again. 2. **`karate.callSingle()` caches the exception.** The stored value is the failure itself, and every later caller has it re-thrown at once. A broken suite-wide sign-in fails the run quickly instead of attempting the same doomed exchange from every scenario. ## Choosing between them - Reach for `callonce` when the expensive thing is genuinely per-file — seeding the records this feature asserts on, pulling a reference list this feature needs — and when you want each feature file to stay independently runnable. - Reach for `karate.callSingle()` in `karate-config.js` when the thing is genuinely global, above all a token exchange whose result every feature will attach as a header. - Reach for `karate.setupOnce()` when the data lives in the *same* file, in a `Scenario` tagged `@setup`. One shared caution applies to all three: they cache **data, not behaviour**. Each returns a copy of the cached value, and Karate's own guidance is to keep results to plain JSON. A JavaScript function handed back from a cached call still resolves its variables in the context it was born in — the one real execution's scope — which is a subtle source of wrong values in whichever scenario later receives it.
- In Karate, why does `karate.callSingle()` accept a `?name` suffix on the file path?Because the cache key is the path string, not the argument. Two calls to the same feature with different arguments would otherwise collide on one entry and the second would silently receive the first one's result. Appending `?admin` and `?user` gives two keys; the suffix is stripped before the file is read, so both calls execute the same feature.
- In Karate, why is `karate.callSingle()` usually written in `karate-config.js` rather than in a feature file?The config chain is re-evaluated for every top-level scenario, so it is the one place that runs before everything and can hand every feature the same value. `callSingle` makes that per-scenario evaluation cheap by executing the target once for the run. It works inside a feature file too, but then only the features that call it benefit.
- In Karate, do `callonce` and `karate.callSingle()` behave the same way when the thing they call fails?No, and the difference is deliberate. `callonce` writes to its cache only after a successful call, so a failure is not cached and the next scenario retries. `karate.callSingle()` stores the exception and re-throws it to every later caller, so a broken global sign-in fails the run fast rather than being attempted from every scenario.
saying these in an interview costs you the question
- Says callSingle is just callonce with a longer lifetime
- Thinks the callSingle key includes the argument
- Claims callonce is shared across all feature files
- Expects a callsingle step keyword to exist
- Says karate-config.js runs once per suite
- Treats both caches as safe for JS functions