Skip to main content

REUSABILITY

Calling Features

Reuse authentication flows, test data setup, and common operations by calling feature files and JavaScript functions. Variables and configuration from the caller are automatically available in called features.

On this page:

Calling Feature Files

Use call read() to execute another feature file and capture results:

Gherkin
Feature: Basic feature calling

Background:
* def signIn = call read('classpath:auth/login.feature') { username: 'john', password: 'secret' }
* def authToken = signIn.authToken

Scenario: Use authentication token
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
And header Authorization = 'Bearer ' + authToken
When method get
Then status 200

The called feature returns all variables defined with def as keys in a JSON-like object.

Called Feature Example

Here's what the called feature looks like:

login.feature
@ignore
Feature: Login helper

Scenario:
Given url 'https://httpbin.org'
And path 'post'
And request { userId: '#(username)', userPass: '#(password)' }
When method post
Then status 200
* def authToken = response.json
What Gets Passed
  • All variables and configure settings are passed to called features
  • You cannot use * url 'http://...' and expect it in the called feature - use a variable instead: * url myUrl
  • Use configure url or pass the URL as a parameter for dynamic URL handling

Passing Parameters

Pass data using a single JSON argument. The keys in the JSON become variables in the called feature:

caller.feature
Feature: Passing parameters to features

Scenario: Pass credentials via variable
* def credentials = { username: 'admin', password: 'secret123' }
* def authResult = call read('classpath:auth/login.feature') credentials
* print authResult.authToken

Scenario: Pass credentials inline
* def authResult = call read('classpath:auth/login.feature') { username: 'john', password: 'secret' }
* print authResult.authToken
login.feature
@ignore
Feature: Login helper

Scenario:
# username and password are available as variables from the JSON argument
Given url loginUrlBase
And request { userId: '#(username)', userPass: '#(password)' }
When method post
Then status 200
* def authToken = response

The JSON argument keys (username, password) become variables that can be used with embedded expressions #(variableName) in the called feature.

Default Values in Called Features

Use karate.get() to provide defaults for optional parameters:

user-setup.feature
Feature: User setup with defaults

Scenario:
# Provide defaults if parameters not passed
* def username = karate.get('username', 'testuser')
* def role = karate.get('role', 'user')
* def retries = karate.get('retries', 3)

Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
And request { username: '#(username)', role: '#(role)' }
When method post
Then status 201

Built-in Variables

Access call arguments and loop state using built-in variables:

VariableDescription
__argThe entire argument object passed to call (null if none). Typically not needed since JSON keys become variables directly.
__loopCurrent iteration index when called with array (starts at 0, -1 if not in loop)

Data-Driven Calling

When you pass a JSON array, the feature is called once per element:

Gherkin
Feature: Data-driven feature calling

Scenario: Create multiple users
* table users
| name | role |
| 'John' | 'admin' |
| 'Jane' | 'user' |
| 'Bob' | 'guest' |

* def results = call read('create-user.feature') users

# results is an array of 3 result objects
* match results == '#[3]'
* def responses = $results[*].response
* match each responses contains { id: '#number' }
create-user.feature
@ignore
Feature: Create single user

Scenario:
# name and role are directly available as variables from each array element
# __loop contains the current iteration index (0, 1, 2, ...)
Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
And request { name: '#(name)', role: '#(role)' }
When method post
Then status 201

Tag Selectors

Call specific scenarios by tag:

Gherkin
Feature: Tag selectors

Scenario: Call by tag
# Call scenarios with @setup tag
* call read('classpath:common.feature@setup')

# Call scenario by name
* call read('classpath:workflows.feature@name=createUser')

Call Same Feature

Call another scenario within the same feature file using just the tag:

Gherkin
Feature: Same-file calling

Scenario: Main test
# No assignment = shared scope, so 'result' merges into this scenario
* call read('@helper')
* match result == 'from helper'

@ignore @helper
Scenario: Helper scenario
* def result = 'from helper'

The copy Keyword

Objects are passed by reference. Use copy to clone data and prevent mutation:

Gherkin
Feature: Copy keyword example

Scenario: Prevent mutation
* def originalData = { id: 1, items: ['a', 'b'] }

# Create a clone using 'copy' keyword
* copy clonedData = originalData

# Pass the clone - original won't be modified by called feature
* def result = call read('modify-data.feature') clonedData

# Original unchanged even if called feature mutated the data
* match originalData.items == ['a', 'b']

Shared Scope

When call is used without assigning to a variable, variables from the called feature merge into the caller's scope:

Gherkin
Feature: Shared scope example

Background:
# No assignment - variables merge into this feature's scope
* call read('classpath:common-setup.feature') { env: 'test' }
# authToken, baseUrl, etc. now available here

Scenario: Use shared variables
Given url baseUrl
And path 'users', 1
And header Authorization = 'Bearer ' + authToken
When method get
Then status 200
common-setup.feature
@ignore
Feature: Common setup

Scenario:
* def baseUrl = 'https://jsonplaceholder.typicode.com'
* def authToken = 'test-token-123'
* configure headers = { 'X-Custom': 'value' }
When to Use Shared Scope
  • Authentication setup that sets headers for all requests
  • Common configuration that multiple scenarios need
  • One-liner setup calls in Background

Use with care - called features can overwrite existing variables.

Isolated vs Shared Scope

PatternScopeUse Case
def result = call read('f.feature')IsolatedGet result, don't affect caller variables
call read('f.feature')SharedMerge all variables into caller
karate.call('f.feature')IsolatedJS API form, same as first pattern
karate.call(true, 'f.feature')SharedJS API with shared scope

callonce

Execute a feature only once per feature file, caching results for all scenarios:

Gherkin
Feature: One-time setup

Background:
# Runs only once, even with multiple scenarios
* def authData = callonce read('classpath:auth/expensive-login.feature')
* def token = authData.token

Scenario: First test
Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
And header Authorization = 'Bearer ' + token
When method get
Then status 200

Scenario: Second test
# authData and token still available - no re-execution
Given url 'https://jsonplaceholder.typicode.com'
And path 'posts'
And header Authorization = 'Bearer ' + token
When method get
Then status 200

Use callonce for:

  • Authentication token generation
  • Database initialization
  • Test data creation
  • Environment setup
callonce Restrictions

callonce should return pure JSON data. Avoid returning JavaScript functions or complex Java objects - they may cause issues in parallel execution.

karate.callSingle()

Execute a feature once globally across all test files, even in parallel execution. Use in karate-config.js:

karate-config.js
function fn() {
var env = karate.env || 'dev';

// Runs once across ALL features
var auth = karate.callSingle('classpath:auth/global-login.feature', { env: env });

return {
baseUrl: auth.baseUrl,
authToken: auth.token
};
}
Gherkin
Feature: Using global authentication

Scenario: Use globally cached auth
# authToken available from karate-config.js
Given url baseUrl
And path 'users', 1
And header Authorization = 'Bearer ' + authToken
When method get
Then status 200

Multiple callSingle Calls

The first argument is used as the cache key. To call the same feature with different arguments, add a ?name suffix:

karate-config.js
// Different cache keys for different users
var adminAuth = karate.callSingle('classpath:auth/login.feature?admin', { username: 'admin', password: 'admin123' });
var userAuth = karate.callSingle('classpath:auth/login.feature?user', { username: 'user', password: 'user456' });

Dev Mode Caching

Cache callSingle results to disk during development to avoid repeated auth calls:

karate-config.js
function fn() {
if (karate.env == 'local') {
// Cache for 15 minutes
karate.configure('callSingleCache', { minutes: 15 });
}

var auth = karate.callSingle('classpath:auth/login.feature');
return { token: auth.token };
}
callSingle Restrictions

Return only pure JSON data (strings, numbers, objects, arrays). Do not return:

  • JavaScript functions
  • Java objects
  • Complex objects with circular references

These can cause issues in parallel execution due to caching and serialization.

JavaScript Functions

Define reusable JavaScript functions:

Gherkin
Feature: JavaScript functions

Background:
* def calculateTotal =
"""
function(items) {
var sum = 0;
for (var i = 0; i < items.length; i++) {
sum += items[i].price * items[i].quantity;
}
return { subtotal: sum, tax: sum * 0.08, total: sum * 1.08 };
}
"""

Scenario: Use function
* def items = [{ price: 100, quantity: 2 }, { price: 50, quantity: 1 }]
* def totals = call calculateTotal items
* match totals.subtotal == 250

Direct Function Calls

Call functions directly with multiple arguments (no call keyword needed):

Gherkin
Feature: Direct function calls

Scenario: Call functions with multiple arguments
* def multiply = function(a, b) { return a * b }
* def formatName = function(first, last) { return first + ' ' + last }

* def result = multiply(5, 10)
* match result == 50

* def fullName = formatName('John', 'Doe')
* match fullName == 'John Doe'

Function Return Behavior

When a function returns a map-like object:

Gherkin
Feature: Function return behavior

Scenario: Return value behavior
* def getConfig = function() { return { host: 'jsonplaceholder.typicode.com', port: 443 } }

# Assigned - variables stay in result object
* def config = call getConfig
* match config.host == 'jsonplaceholder.typicode.com'

# Not assigned - keys become variables in current scope
* call getConfig
* match host == 'jsonplaceholder.typicode.com'
* match port == 443

Java Interop

Call Java code from Karate:

Gherkin
Feature: Java interop

Scenario: Call static Java method
* def Base64 = Java.type('java.util.Base64')
* def encoded = Base64.getEncoder().encodeToString('hello'.getBytes())
* match encoded == 'aGVsbG8='

Scenario: Call instance method
* def ArrayList = Java.type('java.util.ArrayList')
* def list = new ArrayList()
* list.add('item1')
* list.add('item2')
* match list.size() == 2

Wrapping Java in Functions

For cleaner reuse, wrap Java calls in JavaScript functions:

Gherkin
Feature: Wrapping Java in functions

Background:
* def uuid =
"""
function() {
var UUID = Java.type('java.util.UUID');
return UUID.randomUUID().toString();
}
"""

Scenario: Generate UUID
* def id = uuid()
* match id == '#uuid'

HTTP Basic Auth Example

Combine Java and JavaScript for authentication:

basic-auth.js
function fn(creds) {
var temp = creds.username + ':' + creds.password;
var Base64 = Java.type('java.util.Base64');
var encoded = Base64.getEncoder().encodeToString(temp.toString().getBytes());
return 'Basic ' + encoded;
}
Gherkin
Feature: HTTP Basic Auth

Scenario: Use basic auth
* def basicAuth = read('classpath:basic-auth.js')
* header Authorization = call basicAuth { username: 'john', password: 'secret' }

Given url 'https://httpbin.org'
And path 'get'
When method get
Then status 200

call vs read() Reference

CodeDescription
def result = call read('f.feature')Isolated scope, get result
call read('f.feature')Shared scope, merge variables
call read('f.feature') argPass argument to feature
def f = read('f.feature') then call fStore feature, call later
karate.call('f.feature')JS API, isolated scope
karate.call(true, 'f.feature')JS API, shared scope
def data = read('data.json')Load JSON data (not executed)
def fn = read('utils.js')Load JS file

Next Steps