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
Declare and assign variables using def
:
Feature: Variable basics
Scenario: Basic variable assignment # String variables
* def userName = 'John Doe'
* def message = 'Hello ' + userName
# Number variables
* def userId = 123
* def score = 85.5
# Boolean variables
* def isActive = true
* def isAdmin = false
# JSON objects
* def user = { id: 123, name: 'John', active: true }
* def scores = [85, 92, 78, 90]
# XML documents
* def userXml = <user><id>123</id><name>John</name></user>
The keywords url
and request
cannot be used as variable names in Karate. These are reserved for HTTP operations.
Use alternative names:
- ❌
* def url = 'https://api.example.com'
- ✅
* def apiUrl = 'https://api.example.com'
- ❌
* def request = { name: 'test' }
- ✅
* def requestBody = { name: 'test' }
This prevents confusion between variable assignment and Karate's HTTP keywords.
Multi-Line Expressions
For readability, use triple-quotes for large JSON or XML:
Feature: Multi-line variables
Scenario: Readable multi-line definitions # Multi-line XML * def userXml =
"""
<user>
<id>123</id>
<name>John Doe</name>
<profile>
<email>john@test.com</email>
<verified>true</verified>
</profile>
</user>
"""
# Multi-line JSON
* def requestBody =
"""
{
"user": {
"name": "John",
"email": "john@test.com",
"preferences": {
"theme": "dark",
"notifications": true
}
}
}
"""
* match userXml/user/name == 'John Doe'
* match requestBody.user.name == 'John'
assert - Validation
Use assert
to validate conditions:
Feature: Variable validation
Scenario: Assert variable values * def user = { id: 123, name: 'John', age: 30 }
# Simple assertions
* assert user.id == 123
* assert user.name == 'John'
* assert user.age > 18
# Complex assertions
* assert user.id > 0 && user.name.length > 0
* assert user.age >= 18 && user.age <= 65
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 complex objects (pretty-printed automatically)
* print 'Complete user:', user
* print 'Scores array:', scores
# Print multiple values
* print 'User', user.name, 'has ID', user.id
# Pretty-print helpers for formatting
* 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
Create JSON arrays from table data:
Feature: Table to JSON conversion
Scenario: Create user data with table * table users
| name | age | active |
| 'John' | 30 | true |
| 'Jane' | 25 | true |
| 'Bob' | 35 | false |
# Result: [{"name":"John","age":30,"active":true}, ...]
* match users == '#[3] #object'
* match users[0].name == 'John'
* match users[1].age == 25
Scenario: Dynamic table expressions
* def one = 'hello'
* def two = { value: 'world' }
- table data
| foo | bar |
| one | { count: 1 } |
| two.value | ['a', 'b'] |
# Variables and expressions are evaluated
* match data[0].foo == 'hello'
* match data[1].foo == 'world'
Empty cells omit the key from JSON. Use (null)
to force a null value.
* table data
| name | email |
| 'John' | | # Result: { name: 'John' } - email omitted
| 'Jane' | (null) | # Result: { name: 'Jane', email: null }
get - JsonPath Operations
get and $variable Shortcut
Extract data using get
keyword or $
shortcut:
Feature: JsonPath extraction
Scenario: Extract data with get or $ shortcut * def response = {
users: [
{ id: 1, name: 'John', profile: { email: 'john@test.com' } },
{ id: 2, name: 'Jane', profile: { email: 'jane@test.com' } }
],
total: 2
}
# Basic property access
* def total = response.total
* def firstUser = response.users[0]
* def userName = response.users[0].name
# JsonPath with get keyword
* def userNames = get response.users[*].name
* match userNames == ['John', 'Jane']
# JsonPath with $ shortcut (more concise)
* def userEmails = $response.users[*].profile.email
* match userEmails == ['john@test.com', 'jane@test.com']
# In match statements, $ is optional
* match response.users[*].id == [1, 2]
get with Index
Access array elements safely with optional defaults:
Feature: Safe array access
Scenario: Get with index operations * def users = [
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' },
{ id: 3, name: 'Bob' }
]
# Safe array access with karate.get()
* def firstUser = karate.get('users[0]')
* def secondUser = karate.get('users[1]')
* def missingUser = karate.get('users[10]') # Returns null safely
# With default values
* def user = karate.get('users[5]', { id: 0, name: 'Default' })
* match user.name == 'Default'
# Extract first element from JsonPath array result
* def firstId = get[0] users[*].id
* match firstId == 1
JsonPath Filters
Filter arrays using JsonPath expressions:
Feature: JsonPath filtering
Scenario: Filter data with JsonPath * 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 property value
* def electronics = karate.jsonPath(products, "$[?(@.category=='electronics')]")
* match electronics == '#[2] #object'
# Filter by price range
* def expensive = karate.jsonPath(products, "$[?(@.price > 500)]")
* match expensive == '#[2] #object'
# Get specific fields
* def productNames = karate.jsonPath(products, "$[*].name")
* match productNames == ['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 # Start with basic user data * def user = { id: 123, name: 'John', profile: { email: 'john@test.com' } }
# Add properties based on test conditions
* user.active = true
* user.lastLogin = new Date().toISOString()
# Update nested objects (common with API responses)
* user.profile.verified = true
* user.profile.phone = '+1-555-0123'
# Add arrays for permissions or tags
* user.permissions = ['read', 'write']
* user.tags = []
# Verify the modifications worked
* match user.active == true
* match user.profile.verified == true
# Now user object is ready for API request
* print 'Final payload:', 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
Understand reference vs copy behavior to avoid unexpected data changes in your tests:
Feature: Variable copying
Scenario: Understand reference vs copy behavior * def originalUser = { id: 1, data: { value: 'test' } }
# DANGER: This creates a reference, not a copy
* def userReference = originalUser
* userReference.id = 999
* match originalUser.id == 999 # Surprise! Original was modified
* print 'Original user was changed:', originalUser
# SAFE: Use karate.copy() for independent copy
* def baseUser = { id: 1, data: { value: 'test' } }
* def independentUser = karate.copy(baseUser)
* independentUser.id = 999
* match baseUser.id == 1 # Original remains unchanged
* print 'Base user stayed the same:', baseUser
* print 'Independent user:', independentUser
* def newVar = existingVar
creates a reference, not a copy. Changes to newVar
will modify existingVar
too!
- Creating test variations from a base object
- Modifying API responses without affecting original data
- Building multiple similar request payloads
- Any time you need an independent copy
Working with Complex Data
Nested Object Access
Feature: Nested data access
Scenario: Work with complex JSON structures * def response = {
user: {
id: 123,
profile: {
personal: { name: 'John', age: 30 },
contact: { email: 'john@test.com', phone: '555-0123' }
},
preferences: {
theme: 'dark',
notifications: { email: true, sms: false }
}
}
}
# Direct property access
* def userId = response.user.id
* def userName = response.user.profile.personal.name
* def userEmail = response.user.profile.contact.email
# Safe access with get
* def theme = karate.get('response.user.preferences.theme')
* def missing = karate.get('response.user.preferences.language', 'en')
* match userId == 123
* match userName == 'John'
* match missing == 'en'
Array Manipulation
Feature: Array operations
Scenario: Array manipulation techniques
* def numbers = [1, 2, 3, 4, 5]
* def users = [
{ name: 'John', age: 30 },
{ name: 'Jane', age: 25 },
{ name: 'Bob', age: 35 }
]
# Array operations
* def doubled = numbers.map(x => x * 2)
* def evens = numbers.filter(x => x % 2 == 0)
* def sum = numbers.reduce((a, b) => a + b, 0)
# Object array operations
* def names = users.map(u => u.name)
* def adults = users.filter(u => u.age >= 30)
* def totalAge = users.reduce((sum, u) => sum + u.age, 0)
* match doubled == [2, 4, 6, 8, 10]
* match names == ['John', 'Jane', 'Bob']
* match totalAge == 90
Common Gotchas
- Variable overwrites:
def
replaces any existing variable with the same name - Reserved names: Cannot use
url
orrequest
as 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