Skip to main content

ADVANCED

Hooks and Lifecycle

Control test initialization, cleanup, and resource management using Karate's lifecycle hooks without requiring Java code.

On this page:

Hook Types Overview

HookWhen It RunsScope
karate-config.jsBefore every ScenarioGlobal
BackgroundBefore every Scenario in FeatureFeature
callonce in BackgroundOnce per FeatureFeature
karate.callSingle()Once globally (thread-safe)Global
configure beforeScenarioBefore every Scenario (before Background)Scenario
configure afterScenarioAfter every Scenario (pass or fail)Scenario
configure afterFeatureAfter all Scenarios in FeatureFeature
configure onStepFailureWhen a Gherkin step failsStep

Background Setup

Use Background to run setup before every scenario in a feature:

Gherkin
Feature: User API tests

Background:
* url 'https://jsonplaceholder.typicode.com'
* def authToken = 'Bearer abc123'
* header Authorization = authToken

Scenario: Get user
Given path 'users', 1
When method get
Then status 200

Scenario: Get all users
Given path 'users'
When method get
Then status 200
And match response == '#[10]'

The Background runs before each scenario, ensuring consistent setup.

One-Time Feature Setup

Use callonce to execute expensive operations once per feature:

Gherkin
Feature: API tests with authentication

Background:
# Runs once per feature, cached for all scenarios
* def authData = callonce read('classpath:auth/login.feature')
* def token = authData.token
* url 'https://jsonplaceholder.typicode.com'
* header Authorization = 'Bearer ' + token

Scenario: First test
Given path 'users'
When method get
Then status 200

Scenario: Second test
Given path 'posts'
When method get
Then status 200
callonce vs call
  • call executes every time it's encountered
  • callonce executes once and caches the result for all scenarios in the feature

Before Scenario Hook

Execute setup logic before each scenario. Because beforeScenario fires before the Background, it must be configured in karate-config.js to take effect — setting it inside Background is a no-op for the current scenario.

karate-config.js
function fn() {
karate.configure('beforeScenario', function() {
karate.log('Starting:', karate.scenario.name);
});
return {};
}

For mock servers, beforeScenario can be set in the mock's Background and runs per request — useful for per-request setup since a mock's Background only runs once at server init.

After Scenario Hook

Execute cleanup logic after each scenario. The hook runs on both pass and fail paths, so teardown always executes:

Gherkin
Feature: After scenario cleanup

Background:
* configure afterScenario =
"""
function() {
karate.log('Scenario completed:', karate.scenario.name);
if (karate.info.errorMessage) {
karate.log('FAILED:', karate.info.errorMessage);
}
}
"""

Scenario: Test user endpoint
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200

Available Properties in Hooks

Access scenario information within hooks:

PropertyDescription
karate.scenario.nameCurrent scenario name
karate.info.errorMessageError message if scenario failed (null if passed)
karate.info.scenarioTypeType: Scenario or Scenario Outline

Hook Failures

A hook exception fails the enclosing scenario — the same convention that applies to regular steps. This surfaces bugs in teardown code that would otherwise be silently logged. Wrap the hook body in try/catch to suppress:

Gherkin
Background:
* configure afterScenario =
"""
function() {
try {
karate.call('classpath:cleanup.feature');
} catch (e) {
karate.log('cleanup failed (suppressed):', e);
}
}
"""

For beforeScenario, a hook failure halts the scenario by default. Set continueOnStepFailure if you want scenario steps to run even after a failed beforeScenario. For mock servers, a hook failure (either beforeScenario or afterScenario) surfaces as an HTTP 500 response with the error in the body.

After Feature Hook

Configure feature-level cleanup after all scenarios complete:

Gherkin
Feature: After feature cleanup

Background:
* configure afterFeature =
"""
function() {
karate.log('All scenarios in feature completed');
}
"""

Scenario: First scenario
* def result = 1 + 1
* match result == 2

Scenario: Second scenario
* def result = 2 + 2
* match result == 4
afterFeature Requirements

The afterFeature hook only works with the Runner API for parallel execution, not the JUnit runner.

// Required for afterFeature hooks to work
SuiteResult results = Runner.path("classpath:features").parallel(5);

After Scenario Outline Hook

Handle cleanup after all rows of a data-driven test complete:

Gherkin
Feature: Scenario Outline hooks

Background:
* configure afterScenario =
"""
function() {
karate.log('Row completed');
}
"""
* configure afterScenarioOutline =
"""
function() {
karate.log('All rows completed');
}
"""

Scenario Outline: Validate users
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', <id>
When method get
Then status 200

Examples:
| id |
| 1 |
| 2 |
| 3 |
  • afterScenario runs after each Examples row
  • afterScenarioOutline runs once after all rows complete

On Step Failure Hook

Fires when a Gherkin step fails — match, assert, HTTP error, driver error, anything. One hook handles every failure type. Use it to attach diagnostic artifacts (screenshots, logs, page state) directly to the failed step in the report, and to flip the static continueOnStepFailure decision on a per-step basis.

Gherkin
Background:
* configure onStepFailure =
"""
function(info) {
karate.log('step failed:', info.error)
karate.log('at:', info.featureName, '/', info.scenarioName, 'line', info.step.line)
info.embed('diagnostic note', 'text/plain', 'why-it-failed.txt')
}
"""

The hook receives a single info object:

PropertyDescription
info.errorError message from the failed step
info.step{ line, text, prefix } describing the failed step
info.scenarioNameCurrent scenario name
info.featureNameCurrent feature name
info.embed(bytes, mime, name?)Attach an artifact (PNG, log, JSON, …) directly to the failed step in the HTML report
info.proceed()Per-step override: convert this hard failure into a soft assertion (execution continues)
info.stop()Per-step override: convert this soft assertion (from continueOnStepFailure = true) into a hard stop

Per-step soft / hard assertion override

configure continueOnStepFailure is a static all-or-nothing flag. info.proceed() and info.stop() give you per-step control. Call neither and the runtime falls back to whatever continueOnStepFailure is currently set to.

Gherkin
# Soft-assert only match steps; stop on any other failure (e.g. HTTP, driver)
Background:
* configure onStepFailure =
"""
function(info) {
if (info.step.text && info.step.text.indexOf('match ') === 0) {
info.proceed() // record the failure but keep going
}
}
"""

Scenario: Validate everything before bailing out
* match response.name == 'Alice'
* match response.age == 30
* match response.role == 'admin'
# any of the three above can fail and the scenario keeps running
# but a failing HTTP request would still halt — info.proceed() not called for those

Attaching screenshots from a failed UI step

The driver already auto-captures a screenshot when a UI step fails (see Screenshots on failure). Use onStepFailure to layer additional artifacts — page HTML, console logs, network HARs — alongside the auto-screenshot:

Gherkin
Background:
* configure driver = { type: 'chrome' }
* configure onStepFailure =
"""
function(info) {
try {
var html = driver.script('document.documentElement.outerHTML')
info.embed(html, 'text/html', 'page-snapshot.html')
} catch (e) {
karate.log('snapshot failed (driver may not be alive):', e)
}
}
"""

Behavior

  • Innermost failure only. When a called feature fails, the hook fires in the callee, not again in the caller (mirrors the auto-screenshot suppression).
  • Errors are swallowed. A throw inside onStepFailure is warn-logged and never escalates into a second scenario failure — the original step failure is what surfaces.
  • @report=false scenarios skip the hook's event-bus side-effect to keep sensitive content out of report artefacts.
  • All step types fire it — match, assert, HTTP error, driver error. One hook, many failure sources.

Configuration File

karate-config.js

Configure global settings in karate-config.js (processed before every scenario):

karate-config.js
function fn() {
var env = karate.env || 'dev';
karate.log('Running in environment:', env);

var config = {
baseUrl: 'https://jsonplaceholder.typicode.com'
};

if (env === 'prod') {
config.baseUrl = 'https://api.production.com';
}

// Set timeouts globally
karate.configure('connectTimeout', 5000);
karate.configure('readTimeout', 5000);

return config;
}

Variables returned in config are available to all scenarios.

Environment-Specific Config

Create karate-config-<env>.js files to override settings per environment:

karate-config-dev.js
function fn() {
return {
baseUrl: 'http://localhost:8080',
debugMode: true
};
}

Run with: -Dkarate.env=dev

Global One-Time Setup

Use karate.callSingle() for initialization that runs once globally, even across parallel threads:

karate-config.js
function fn() {
var config = {
baseUrl: 'https://jsonplaceholder.typicode.com'
};

// Runs once globally, even in parallel execution
var auth = karate.callSingle('classpath:auth/get-token.feature');
config.authToken = auth.token;

return config;
}
callonce vs karate.callSingle()
  • callonce runs once per feature
  • karate.callSingle() runs once globally across all features, even in parallel

Multiple Cache Keys

Use different cache keys for different setups:

karate-config.js
function fn() {
var config = {};

// Different cache keys create separate cached results
var adminAuth = karate.callSingle('classpath:auth.feature?admin', {
username: 'admin',
password: 'admin123'
});

var userAuth = karate.callSingle('classpath:auth.feature?user', {
username: 'testuser',
password: 'user123'
});

config.adminToken = adminAuth.token;
config.userToken = userAuth.token;

return config;
}

The ?admin and ?user suffixes create separate cache entries.

Development Mode Caching

Cache authentication tokens to disk for faster development cycles:

karate-config.js
function fn() {
var config = {};

// Enable disk caching in dev mode
if (karate.env === 'dev') {
karate.configure('callSingleCache', { minutes: 15, dir: 'target/auth-cache' });
}

// This result will be cached to disk in dev mode
var auth = karate.callSingle('classpath:expensive-auth.feature');
config.authToken = auth.token;

return config;
}
Pure JSON Only

karate.callSingle() caching requires pure JSON return values. Avoid returning JavaScript functions or Java objects - they cause caching issues in parallel execution.

Hook Summary Table

To Run CodeHow
Before everything (globally once)karate.callSingle() in karate-config.js
Before every ScenarioBackground section, karate-config.js, or configure beforeScenario
Once per Featurecallonce in Background
After every Scenarioconfigure afterScenario
After every Scenario Outlineconfigure afterScenarioOutline
After all Scenarios in Featureconfigure afterFeature (requires Runner API)
When a Gherkin step failsconfigure onStepFailure

Next Steps