Q10 of 40 · Karate
What is a Karate feature reference (call read('classpath:...')) and when is it useful?
KarateMidkaratecall-readfeature-compositionhelpersreuse
Short answer
Short answer: call read('classpath:path/to/feature.feature') executes another feature file inline and returns its exported variables as a map. Pass arguments as a JSON object after the path. Use it to share authentication flows, reusable data-setup sequences, or parameterised sub-scenarios across feature files.
Detail
call read is Karate's mechanism for feature-file composition:
# Call with no args
* def authResult = call read('classpath:helpers/login.feature')
* def token = authResult.accessToken
# Call with arguments (the called feature receives them as variables)
* def user = call read('classpath:helpers/create-user.feature')
{ name: 'Alice', email: 'alice@example.com' }
* def userId = user.id
Inside the called feature (create-user.feature):
Feature: Create user helper
Scenario:
Given url baseUrl
And path '/users'
And request { name: '#(name)', email: '#(email)' }
When method POST
Then status 201
* def id = response.id # exported — available to caller
The #(name) syntax embeds a Karate variable into a JSON string — called "embedded expressions".
When to use call read:
- Authentication (login.feature) called from Background in every feature
- Test data setup (create-user.feature, create-order.feature) called in @BeforeEach equivalent
- Shared sub-scenarios called from a Scenario Outline
callonce — like call but caches the result for the duration of the suite. Ideal for expensive setup (token fetch, seeding reference data) that should run once.
// EXAMPLE
order-flow.feature
Feature: Order flow test
Background:
* url karate.properties['api.base.url']
# callonce — fetches token once for the entire suite (cached)
* def auth = callonce read('classpath:helpers/login.feature')
* header Authorization = 'Bearer ' + auth.token
Scenario: Create and fulfil an order
# Set up a user (call — runs fresh each scenario)
* def newUser = call read('classpath:helpers/create-user.feature')
{ name: 'Alice', email: 'a-' + karate.random(1000) + '@test.com' }
# Create order for that user
* def order = call read('classpath:helpers/create-order.feature')
{ userId: newUser.id, sku: 'WIDGET-1', qty: 2 }
# Fulfil it
Given path '/orders/' + order.id + '/fulfil'
When method POST
Then status 200
And match response.status == 'FULFILLED'// WHAT INTERVIEWERS LOOK FOR
Understanding call vs callonce (fresh vs cached), how arguments are passed and how variables are exported, and the practical patterns (auth helper, data setup helper). The embedded expression #(variable) syntax in request bodies is a real detail.
// COMMON PITFALL
Using call instead of callonce for an expensive login feature — every scenario re-authenticates, adding latency. callonce is the right choice for suite-wide shared resources.