skip to content

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%

answer

  1. A tag that hides a scenario
  2. The setup lives in the same file
  3. Cached per feature, keyed by an optional name
  4. It cannot see the Background

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.

solid answer

~50 s

`karate.setupOnce([name])` executes the `Scenario` in the **same feature file** that carries the built-in `@setup` tag, and hands back a map of the variables that scenario created. The result is cached for the running feature, keyed on the optional name, so a second call anywhere in that file returns a copy rather than re-running it. Two things distinguish it from `callonce`. First, the target is local: no second file, no `read(...)`, so the setup lives beside the thing it sets up. Second, a `@setup` scenario is invisible to normal selection — it is never picked up by an ordinary run or a tag filter, and `karate.setup()` or `karate.setupOnce()` is the only way to execute it. It also runs with the `Background` skipped, so it cannot depend on anything the `Background` defines. If there is more than one, tag them `@setup=name` and pass the name.

code

gherkin · 16 lines
gherkin
Feature: kittens

Background:
  * url baseUrl

@setup=kittens
Scenario:
  * def data = [{ name: 'Bob' }, { name: 'Nyan' }]

@setup=owners
Scenario:
  * def data = [{ name: 'Ada' }]

Scenario: the seeded kittens are visible
  * def seeded = karate.setupOnce('kittens')
  * match seeded.data[0].name == 'Bob'

go deeper

for a junior

Recall the pair: a Scenario tagged @setup, and karate.setupOnce() as the only cached way to run it from the same file.

for a middle

Explain that the tag suppresses normal selection, that the Background is skipped for it, and that the cache is per feature keyed on the optional name.

for a senior

Choose deliberately between a local @setup scenario and a shared feature behind callonce, based on whether more than one file needs the data.

for a principal

Decide when setup deserves its own file at all, trading a readable local block against duplication once several features need the same arrangement.

## What it is `@setup` is a built-in Karate tag, and `karate.setupOnce()` is the cached way to run the scenario carrying it. The pair exists for setup that belongs *inside* the feature it serves rather than in a separate file: ```gherkin Feature: cats Background: * url baseUrl @setup Scenario: * def data = [{ name: 'Bob' }, { name: 'Nyan' }] Scenario: first cat is present * def seeded = karate.setupOnce() * match seeded.data[0].name == 'Bob' ``` The return value is a map of every variable the `@setup` scenario created, so `karate.setupOnce().data` reaches the array above. ## Three rules that define the tag 1. **A `@setup` scenario is never selected by an ordinary run.** Karate's tag evaluation rejects it outright, the same way it rejects `@ignore`. It does not appear in the run, it does not appear in the count, and no tag expression brings it back. The only way to execute it is `karate.setup()` or `karate.setupOnce()`. 2. **It runs with the `Background` skipped.** The setup runtime is created with the background suppressed, so anything the `Background` defines is simply not there. Setup that needs a base URL or a header must establish it itself. 3. **More than one is allowed, but they need names.** Tag them `@setup=kittens` and `@setup=owners`, then call `karate.setupOnce('kittens')`. Without a name, Karate takes the first `@setup` scenario it finds; with a name that matches nothing you get an explicit failure naming the tag rather than a silent empty map. ## The caching, and how wide it is The cache is held by the running feature and keyed on the name you passed — the unnamed call has its own default slot. So: - every scenario and every `Examples` row of that feature shares the one result; - a copy is returned on each hit, not the cached map itself; - a second feature file has its own cache and runs its own `@setup` scenario; - lookups are guarded so parallel scenarios of the feature cannot execute the setup twice. The uncached sibling `karate.setup()` runs the scenario every time it is called, which is what you want when the setup must be fresh per scenario. ## How it differs from `callonce` | | `karate.setupOnce()` | `callonce` | |---|---|---| | Form | function on the `karate` object | step keyword | | Target | a `@setup` scenario in **this** file | any feature or JS function, usually via `read(...)` | | Cache key | the optional setup name | the call expression as written | | Cache width | the running feature | the running feature | | Visible to a normal run | no, the tag suppresses it | the callee is a separate file anyway | Both are feature-wide, so neither collapses work across the suite — that is `karate.callSingle()`'s job. The real choice between them is about where the setup should live. Pull it into a `@setup` scenario when it is meaningful only to this file and you would rather read it next to the scenarios it feeds. Push it into a separate feature and `callonce` it when two or more files need the same thing, since a `@setup` scenario cannot be shared. ## Failure, and what the report shows A `@setup` scenario is not a test, but it is still a scenario, and that shapes what you see when it goes wrong. A failure inside it surfaces against whichever scenario called `karate.setupOnce()`, because that is the scenario that was executing; the setup’s own steps are attached to the report underneath it rather than appearing as an independent result. Two consequences follow. First, a broken setup makes one scenario look broken and the rest of the feature look fine until they too call it. Second, nothing is cached when the setup throws, so the next caller in that feature runs it again — the same rule `callonce` follows, and the opposite of `karate.callSingle()`, which stores the failure and replays it. If the setup is doing something that must not be attempted twice, that retry is worth designing around. ## Practical notes - Because the tag makes the scenario invisible, a `@setup` block is a good place for steps you never want counted as a test — seeding, arranging, fetching reference data. It is not a place for assertions, because a failure there surfaces as a failure of whichever scenario called it. - The result is data. As with the other once-caches, keep functions and Java objects out of it; what a later scenario receives is a copy of the values, and a function handed across still resolves its variables in the scope it was created in. - The step in the `@setup` scenario runs exactly once per feature under `setupOnce`, which is easy to confirm with a `print`: the `Background` around it will run once per scenario and once per `Examples` row, and the setup line will appear once.

  • In Karate, why can a `@setup` scenario not rely on values defined in the feature's `Background`?
    Because the runtime that executes it is created with the background suppressed — the setup scenario runs on its own, before and outside the per-scenario Background cycle. Anything it needs, such as a base URL or an auth header, it has to establish itself. That is also why the tag is not a general-purpose hook: it is a data-producing scenario, not a before-each.
  • In Karate, what is the difference between `karate.setup()` and `karate.setupOnce()`?
    They execute the same `@setup` scenario and return the same shape — a map of the variables it created. `karate.setup()` runs it on every call; `karate.setupOnce()` consults a cache held by the running feature and keyed on the optional setup name, so the scenario executes once for that file and later calls receive a copy. Use `setup()` when the data must be fresh per scenario.

saying these in an interview costs you the question

  • Thinks a @setup scenario runs as part of a normal run
  • Expects the Background to apply inside the @setup scenario
  • Says setupOnce can target a scenario in another file
  • Confuses setupOnce with a before-each hook
  • Assumes setupOnce is cached for the whole suite
  • Puts assertions in the @setup scenario