Skip to main content

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>
Reserved Variable Names

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
Use match instead

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.

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)
Comma vs Concatenation

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 and Null Values

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
When to Use
  • 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
Reference Trap

* def newVar = existingVar creates a reference, not a copy. Changes to newVar will modify existingVar too!

When to Use karate.copy()
  • 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 or request as variable names
  • Reference vs copy: Objects are passed by reference unless explicitly copied
Background Variable Scope

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