CORE SYNTAX
Variables and Assignment
Overview
Karate provides powerful variable management capabilities for dynamic test scenarios. Variables can store strings, numbers, JSON objects, XML documents, and even JavaScript functions. Understanding variable scope, assignment, and manipulation is essential for effective test automation.
Variable Declaration with def
Basic Assignment
# String assignment
* def myVar = 'world'
* def message = 'Hello ' + myVar
# Number assignment
* def myNum = 5
* def calculation = myNum * 2
# Boolean assignment
* def isValid = true
JSON and XML Assignment
# JSON object (lenient parsing - quotes optional for keys)
* def user = { name: 'John', age: 30, active: true }
* def scores = [85, 92, 78]
# XML document
* def xmlDoc = <user><name>John</name><age>30</age></user>
Variable Overwriting and Restrictions
def
overwrites any existing variable with the same name. Configuration variables may already exist before script execution.
Reserved Variable Names:
url
andrequest
are not allowed as variable names to prevent confusion with built-in keywords
# ❌ Invalid - will not set the request body
* def request = { name: 'test' }
# ✅ Valid - proper request body assignment
* def requestBody = { name: 'test' }
* request requestBody
Data Assertion with assert
Boolean Expression Validation
* def color = 'red '
* def num = 5
* assert color + num == 'red 5'
# Complex assertions
* def user = { name: 'John', age: 30 }
* assert user.age >= 18
* assert user.name.length > 0
Best Practice: Use
match
instead ofassert
for JSON/XML comparisons. Match provides better error messages and supports fuzzy matching.
Console Output with print
Basic Printing
# Simple variable printing
* def userName = 'Alice'
* print 'User name is:', userName
# Multiple expressions (comma-separated for pretty-printing)
* def userdata = { name: 'Bob', age: 25 }
* print 'User data:', userdata
Pretty-Printing JSON and XML
* def myJson = { foo: 'bar', baz: [1, 2, 3] }
* print 'JSON structure:', myJson
Output:
20:29:11.290 [main] INFO com.intuit.karate - [print] JSON structure: {
"foo": "bar",
"baz": [
1,
2,
3
]
}
Using Karate Helper Methods
# Manual pretty-printing with line breaks
* print 'Formatted JSON:\n' + karate.pretty(myJson)
# Pretty-print XML
* def xmlData = <root><item>value</item></root>
* print 'Formatted XML:\n' + karate.prettyXml(xmlData)
Data Manipulation with set
Simple Property Updates
* def myJson = { foo: 'bar' }
* set myJson.foo = 'world'
* match myJson == { foo: 'world' }
# Add new properties
* set myJson.newProp = 'value'
* match myJson == { foo: 'world', newProp: 'value' }
JsonPath Expressions
* def data = { users: [] }
# Use JsonPath with $ notation
* set data $.users[0] = { name: 'Alice' }
* set data $.users[] = { name: 'Bob' } # Append to array
* match data.users.length == 2
Nested Object Creation
* def result = {}
* set result.user.profile.name = 'John'
* set result.user.profile.age = 30
* set result.user.permissions = ['read', 'write']
* match result == {
user: {
profile: { name: 'John', age: 30 },
permissions: ['read', 'write']
}
}
Multiple Property Assignment
* def cat = { name: '' }
* set cat
| path | value |
| name | 'Bob' |
| age | 5 |
| tags | ['cute', 'fluffy'] |
* match cat == { name: 'Bob', age: 5, tags: ['cute', 'fluffy'] }
Array Creation with Multiple Columns
* set users
| path | 0 | 1 | 2 |
| name | 'Alice' | 'Bob' | 'Charlie'|
| age | 25 | 30 | 35 |
| role | 'user' | 'admin' | 'user' |
* match users == [
{ name: 'Alice', age: 25, role: 'user' },
{ name: 'Bob', age: 30, role: 'admin' },
{ name: 'Charlie', age: 35, role: 'user' }
]
Nested Path Assignment
# Move parent path next to 'set' to reduce typing
* set user.profile
| path | value |
| firstName | 'John' |
| lastName | 'Doe' |
| contacts[0] | 'john@example.com' |
| contacts[1] | '+1234567890' |
* match user == {
profile: {
firstName: 'John',
lastName: 'Doe',
contacts: ['john@example.com', '+1234567890']
}
}
XML Manipulation
* def cat = <cat><name>Billie</name></cat>
* set cat /cat/name = 'Jean'
* match cat / == <cat><name>Jean</name></cat>
# Set XML fragments
* def xml = <foo><bar>baz</bar></foo>
* set xml/foo/bar = <hello>world</hello>
* match xml == <foo><bar><hello>world</hello></bar></foo>
Data Extraction with get
JsonPath Extraction
* def cat = {
name: 'Billie',
kittens: [
{ id: 23, name: 'Bob' },
{ id: 42, name: 'Wild' }
]
}
# Extract arrays using JsonPath
* def kittenIds = get cat.kittens[*].id
* match kittenIds == [23, 42]
# Alternative JsonPath syntax
* def kittenNames = get cat $.kittens[*].name
* match kittenNames == ['Bob', 'Wild']
JsonPath Short-cuts
# Short-cut form using $ prefix
* def kittenIds = $cat.kittens[*].id
* def kittenNames = $cat.kittens[*].name
# Even shorter - direct match without intermediate variables
* match cat.kittens[*].id == [23, 42]
* match cat.kittens[*].name == ['Bob', 'Wild']
Single Element Extraction
# Get first element from array result
* def firstKittenId = get[0] cat.kittens[*].id
* match firstKittenId == 23
# Equivalent to:
* def allIds = get cat.kittens[*].id
* match allIds[0] == 23
JsonPath Filtering
# Find elements matching criteria
* def bob = get[0] cat.kittens[?(@.id==23)]
* match bob.name == 'Bob'
# Dynamic filtering using karate.jsonPath()
* def searchName = 'Bob'
* def result = karate.jsonPath(cat, "$.kittens[?(@.name=='" + searchName + "')]")[0]
* match result == bob
Data Removal with remove
Property Removal
* def json = { foo: 'world', hey: 'ho', zee: [1, 2, 3] }
* remove json.hey
* match json == { foo: 'world', zee: [1, 2, 3] }
# Remove array elements by index
* remove json $.zee[1]
* match json == { foo: 'world', zee: [1, 3] }
XML Element Removal
* def xml = <foo><bar><hello>world</hello></bar></foo>
* remove xml/foo/bar/hello
* match xml == <foo><bar/></foo>
* remove xml /foo/bar
* match xml == <foo/>
Dynamic Property Removal
# Using JavaScript delete operator
* def key = 'unwanted'
* def data = { wanted: 'keep', unwanted: 'remove' }
* delete data[key]
* match data == { wanted: 'keep' }
Data Table Creation
Table to JSON Array
* table cats
| name | age |
| 'Bob' | 2 |
| 'Wild' | 4 |
| 'Nyan' | 3 |
* match cats == [
{ name: 'Bob', age: 2 },
{ name: 'Wild', age: 4 },
{ name: 'Nyan', age: 3 }
]
Dynamic Table Values
* def one = 'hello'
* def two = { baz: 'world' }
* table json
| foo | bar |
| one | { baz: 1 } |
| two.baz | ['baz', 'ban'] |
* match json == [
{ foo: 'hello', bar: { baz: 1 } },
{ foo: 'world', bar: ['baz', 'ban'] }
]
Handling Null Values
* def nullValue = { baz: null }
* table data
| name | value |
| 'test1' | | # Empty cell = key omitted
| 'test2' | (null) | # Explicit null = null value
| 'test3' | nullValue.baz| # Expression = null value
* match data == [
{ name: 'test1' },
{ name: 'test2', value: null },
{ name: 'test3', value: null }
]
Text Handling
Raw Text Assignment
# Disable JSON/XML parsing with 'text' keyword
* text query =
"""
{
hero(name: "<name>") {
height
mass
}
}
"""
# Useful for GraphQL, templates, or malformed JSON-like text
* def requestBody = { query: '#(query)' }
Placeholder Replacement
# Simple replacement
* def text = 'hello <foo> world'
* replace text.foo = 'bar'
* match text == 'hello bar world'
# Multiple replacements
* def template = 'hello <one> world <two> bye'
* replace template
| token | value |
| one | 'cruel' |
| two | 'good' |
* match template == 'hello cruel world good bye'
# Dynamic replacements with expressions
* def name = 'John'
* def data = { greeting: 'Hi' }
* def text = '<greeting> there <name>!'
* replace text
| token | value |
| greeting | data.greeting |
| name | name |
* match text == 'Hi there John!'
Multi-line Expressions
Readable Complex Data
# Instead of single-line XML
* def cat = <cat><name>Billie</name><scores><score>2</score><score>5</score></scores></cat>
# Use multi-line for better readability
* def cat =
"""
<cat>
<name>Billie</name>
<scores>
<score>2</score>
<score>5</score>
</scores>
</cat>
"""
# Multi-line JSON
* def complexUser =
"""
{
"profile": {
"name": "John Doe",
"preferences": {
"theme": "dark",
"notifications": true
}
},
"permissions": ["read", "write", "admin"]
}
"""
Data Format Conversion
YAML to JSON
# Convert YAML string to JSON
* text yamlString =
"""
name: John
input:
id: 1
subType:
name: Smith
deleted: false
"""
* yaml jsonData = yamlString
* match jsonData == {
name: 'John',
input: {
id: 1,
subType: { name: 'Smith', deleted: false }
}
}
CSV to JSON
# Convert CSV string to JSON array
* text csvString =
"""
name,type
Billie,LOL
Bob,Wild
"""
* csv jsonArray = csvString
* match jsonArray == [
{ name: 'Billie', type: 'LOL' },
{ name: 'Bob', type: 'Wild' }
]
Variable Scope and Lifecycle
Background Variables
Background:
* def globalConfig = { apiUrl: 'https://api.example.com' }
* def authToken = call read('get-auth-token.feature')
Scenario: First test
# globalConfig and authToken are available
* url globalConfig.apiUrl
* header Authorization = 'Bearer ' + authToken
Scenario: Second test
# Fresh copies of Background variables
# Any modifications in previous scenarios are reset
* url globalConfig.apiUrl
Variable Precedence
- Configuration variables (from karate-config.js)
- Background variables (re-initialized per scenario)
- Scenario variables (local to current scenario)
- Called feature variables (returned from feature calls)
Advanced Variable Patterns
Conditional Assignment
* def environment = karate.env || 'dev'
* def baseUrl = environment == 'prod' ? 'https://api.prod.com' : 'https://api.dev.com'
* def timeout = environment == 'prod' ? 30000 : 5000
Variable Templates
# Template with embedded expressions
* def userTemplate = {
id: '#(userId)',
name: '#(userName)',
email: '#(userName + "@example.com")',
timestamp: '#(new Date().getTime())'
}
# Use template with different values
* def userId = 123
* def userName = 'alice'
* def user1 = userTemplate
* def userId = 456
* def userName = 'bob'
* def user2 = userTemplate
Best Practices
Variable Naming
# ✅ Good: Descriptive names
* def userAuthToken = 'bearer-token-123'
* def customerOrderData = { orderId: 'ORD-001' }
# ❌ Avoid: Generic names
* def data = { orderId: 'ORD-001' }
* def temp = 'bearer-token-123'
Data Organization
# ✅ Good: Grouped related data
* def testConfig = {
api: { baseUrl: 'https://api.example.com', timeout: 5000 },
user: { name: 'testuser', role: 'admin' },
features: { enableNewUi: true, enableBeta: false }
}
# ✅ Good: Use meaningful prefixes
* def apiBaseUrl = testConfig.api.baseUrl
* def userRole = testConfig.user.role
Performance Considerations
# ✅ Good: Use callonce for expensive operations
Background:
* def authData = callonce read('expensive-auth-setup.feature')
* def staticConfig = callonce read('load-config.feature')
# ✅ Good: Reuse complex objects
* def baseRequest = { headers: commonHeaders, timeout: 5000 }
* def request1 = karate.merge(baseRequest, { body: data1 })
* def request2 = karate.merge(baseRequest, { body: data2 })
Next Steps
- Learn about Data Types and Formats for advanced JSON/XML handling
- Explore Expressions and JavaScript for dynamic value generation
- Understand Actions and Keywords for test execution control