CORE SYNTAX
Variables
Store any data type - strings, JSON, XML, arrays - with zero boilerplate. Variables power Karate's dynamic testing, from simple values to complex API responses.
Setting and Using Variables
def - Variable Declaration
Use def to declare variables for strings, numbers, booleans, objects, or arrays. Variables help reuse common data across steps.
Feature: Variable basics
Scenario: Working with variables
# Strings
* def name = 'Alice'
* def greeting = 'Hello ' + name
# Numbers and booleans
* def id = 1
* def active = true
# JSON objects and arrays
* def user = { id: id, name: name, active: active }
* def ids = [1, 2, 3]
# XML
* def userXml = <user><id>1</id><name>Alice</name></user>
url and request are reserved keywords in Karate for HTTP operations.
Do not use them as variable names.
Incorrect:
* def url = 'https://api.example.com'
* def request = { name: 'test' }
Correct:
* def apiUrl = 'https://api.example.com'
* def requestBody = { name: 'test' }
Using reserved keywords as variables leads to unclear tests and unexpected behavior.
Multi-Line Expressions
Use triple-quoted strings for large JSON or XML blocks to keep tests readable and maintainable.
Feature: Multi-line variables
Scenario: Define structured data clearly
# Multi-line JSON
* def user =
"""
{
"id": 1,
"name": "Alice",
"email": "alice@example.com"
}
"""
# Multi-line XML
* def userXml =
"""
<user>
<id>1</id>
<name>Alice</name>
</user>
"""
* match user.name == 'Alice'
* match userXml/user/name == 'Alice'
assert - Validation
assert is best for validating simple expressions or variables, especially when comparing numbers or performing logical checks.
Feature: Variable validation
Scenario: Validate user fields
* def user = { id: 123, name: 'John', age: 30 }
# Basic checks
* assert user.id == 123
* assert user.age > 18
# Combined conditions
* assert user.id > 0 && user.name.length > 0
You will rarely need assert in your test scripts. The match keyword is designed for powerful assertions against JSON and XML payloads and provides much better error messages.
print - Debug Output
Use print to output variable values for debugging:
Feature: Variable debugging
Scenario: Print variable values
* def user = { id: 123, name: 'John' }
* def scores = [85, 92, 78]
* def userXml =
"""
<user>
<name>John</name>
</user>
"""
# Print single values
* print 'User ID:', user.id
* print 'User name:', user.name
# Print full objects
* print 'Complete user:', user
* print 'Scores array:', scores
# Print multiple values in one line
* print 'User', user.name, 'has ID', user.id
# Pretty-print helpers
* print 'Formatted JSON:', karate.pretty(user)
* print 'Formatted XML:', karate.prettyXml(userXml)
Use commas (not +) to pretty-print JSON and XML objects automatically.
table - Creating JSON Arrays
Use the table keyword to quickly build JSON arrays for test data.
Feature: Table to JSON conversion
Scenario: User list created from table
Feature: Table to JSON conversion
Scenario: User list created from table
* table users
| name | age | active |
| 'John' | 30 | true |
| 'Jane' | 25 | true |
| 'Bob' | 35 | false |
* match users[0].name == 'John'
* match users[1].age == 25
* match users[2].active == false
The table above becomes:
[
{ "name": "John", "age": 30, "active": true },
{ "name": "Jane", "age": 25, "active": true },
{ "name": "Bob", "age": 35, "active": false }
]
When converting tables to JSON:
• An empty cell means the key is omitted from the resulting object • (null) inserts a null value explicitly
* table users
| name | email |
| John | | # email key omitted
| Jane | (null) | # email set to null
Becomes:
[
{ "name": "John" },
{ "name": "Jane", "email": null }
]
get - JsonPath Operations
get and $variable Shortcut
Karate supports both standard property access and powerful JsonPath expressions to extract values from JSON objects.
Feature: JsonPath extraction
Scenario: Access and extract fields
* def response =
"""
{
"users": [
{ "id": 1, "name": "John", "profile": { "email": "john@test.com" } },
{ "id": 2, "name": "Jane", "profile": { "email": "jane@test.com" } }
],
"total": 2
}
"""
# Direct access
* def total = response.total
* def firstUser = response.users[0]
* def firstName = response.users[0].name
# Using JsonPath with 'get'
* def userNames = get response.users[*].name
* match userNames == ['John', 'Jane']
# The '$' shortcut: equivalent and more concise
* def emails = $response.users[*].profile.email
* match emails == ['john@test.com', 'jane@test.com']
# '$' is optional inside match
* match response.users[*].id == [1, 2]
get with Index
Use karate.get() for safe access when the index may not exist. Use normal array indexing when working with known positions.
Feature: Safe array access
Scenario: Working with array indexes
* def users =
"""
[
{ "id": 1, "name": "John" },
{ "id": 2, "name": "Jane" },
{ "id": 3, "name": "Bob" }
]
"""
# Normal access (fails if index is missing)
* def firstUser = users[0]
* match firstUser.id == 1
# Safe access: returns null if out of range
* def missingUser = karate.get('users[10]')
* match missingUser == null
# Safe access with a default value
* def fallbackUser = karate.get('users[5]', { id: 0, name: 'Unknown' })
* match fallbackUser.name == 'Unknown'
# Extract a single value from an array of values
* def firstId = get[0] users[*].id
* match firstId == 1
JsonPath Filters
Use JsonPath predicates (?()) to filter JSON arrays by property values.
Feature: JsonPath filtering
Scenario: Filter product data
* def products =
"""
[
{ "id": 1, "name": "Laptop", "price": 999, "category": "electronics" },
{ "id": 2, "name": "Book", "price": 20, "category": "books" },
{ "id": 3, "name": "Phone", "price": 599, "category": "electronics" }
]
"""
# Filter by category
* def electronics = get products[?(@.category == 'electronics')]
* match electronics[*].name == ['Laptop', 'Phone']
# Filter by price
* def expensive = get products[?(@.price > 500)]
* match expensive[*].id == [1, 3]
# Extract a single field from all objects
* def names = get products[*].name
* match names == ['Laptop', 'Book', 'Phone']
Advanced Variable Operations
Variable Modification
Modify JSON objects after creation - useful for building complex payloads or updating data from API responses:
Feature: Variable modification
Scenario: Build request payload dynamically
* def user =
"""
{ "id": 123, "name": "John", "profile": { "email": "john@test.com" } }
"""
# Add or update fields
* set user.active = true
* set user.lastLogin = new Date().toISOString()
# Update nested properties
* set user.profile.verified = true
* set user.profile.phone = '+1-555-0123'
# Add arrays
* set user.permissions = ['read', 'write']
* set user.tags = []
# Validate changes
* match user.active == true
* match user.profile.verified == true
# Payload ready for request
* print user
- Building complex request payloads step by step
- Updating data received from API responses
- Adding conditional fields based on test logic
- Preparing test data with dynamic values
Variable Copying
Assigning one variable to another creates a reference, meaning changes will affect both.
Use karate.toJson() to create an independent deep copy before modifying data.
Feature: Variable copying
Scenario: Reference vs copy behavior
* def original =
"""
{ "id": 1, "data": { "value": "test" } }
"""
# Reference assignment shares data
* def refUser = original
* set refUser.id = 999
* match original.id == 999
# Deep copy using karate.toJson()
* def base =
"""
{ "id": 1, "data": { "value": "test" } }
"""
* def copied = karate.toJson(base)
* set copied.id = 999
* match base.id == 1
* def newVar = existingVar creates a reference, not a copy. Changes to newVar will modify existingVar too.
- Creating variations from a base payload
- Modifying API responses for reuse
- Avoiding hidden data mutation across tests
- Running tests in parallel with shared input
Common Gotchas
- Variable overwrites:
defreplaces any existing variable with the same name - Reserved names: Cannot use
urlorrequestas variable names - Reference vs copy: Objects are passed by reference unless explicitly copied
Variables defined in Background are reset before every Scenario. Do not expect values modified in one scenario to be visible in another scenario.
Why: Scenarios must be isolated and runnable independently, especially for parallel execution.
For one-time setup across all scenarios, use callonce instead:
Background:
* def authToken = callonce read('get-auth-token.feature')
Next Steps
Master variable management and continue with:
- Data Types - Handle JSON, XML, and type conversions
- Expressions - Advanced expressions and JavaScript
- Actions - Use variables with Karate's built-in keywords