CORE SYNTAX
Expressions
Execute JavaScript functions, query data with XPath and JsonPath, and embed dynamic expressions directly in JSON and XML.
Benefits of Expressions
- JavaScript integration: Use full JavaScript including ES6 features for complex logic
- Query languages: Extract data with XPath for XML and JsonPath for JSON
- Embedded expressions: Inject dynamic values using
#(expression)
syntax - Reusable functions: Define JavaScript functions for data transformation and validation
- Built-in utilities: Access Karate's utility functions for filtering, mapping, and data manipulation
Simple Expressions
Execute JavaScript directly for calculations, string operations, and basic conditional logic.
Feature: Basic JavaScript expressions
Scenario: Variables and calculations
* def firstName = 'John'
* def lastName = 'Doe'
* def fullName = firstName + ' ' + lastName
* match fullName == 'John Doe'
* def price = 99.99
* def quantity = 3
* def total = price * quantity
* match total == 299.97
Scenario: Simple conditionals
* def age = 30
* def isAdult = age >= 18
* def status = age >= 18 ? 'adult' : 'minor'
* match isAdult == true
* match status == 'adult'
* def score = 85
* def grade = score >= 90 ? 'A' : score >= 80 ? 'B' : 'C'
* match grade == 'B'
Embedded Expressions
Inject dynamic values into JSON and XML structures using #(expression)
syntax without string concatenation.
Feature: Embedded expressions
Scenario: Basic embedded values
* def userName = 'John'
* def userId = 123
# Embed variables into JSON
* def user = { id: '#(userId)', name: '#(userName)' }
* match user.id == 123
* match user.name == 'John'
Scenario: Embedded calculations and logic
* def basePrice = 100
* def isVip = true
* def order =
{
price: '#(basePrice)',
tax: '#(basePrice * 0.08)',
discount: '#(isVip ? 0.15 : 0.05)',
total: '#(basePrice + (basePrice * 0.08) - (basePrice * 0.15))'
}
* match order.tax == 8
* match order.discount == 0.15
* match order.total == 93
Embedded vs Enclosed
Choose between #(expression)
for mixing static and dynamic values, or ({ ... })
for fully dynamic structures.
Feature: Embedded vs enclosed JavaScript
Scenario: Compare both approaches
* def userName = 'John'
* def userAge = 30
# Embedded: mix static JSON with dynamic values
* def embedded = { name: '#(userName)', age: '#(userAge)', email: '#(userName.toLowerCase() + "@example.com")' }
# Enclosed: entire object is JavaScript
* def enclosed = ({ name: userName, age: userAge, email: userName.toLowerCase() + '@example.com' })
# Both produce identical results
* match embedded == enclosed
Use embedded #(expression)
when you have mostly static JSON with a few dynamic values. Use enclosed ({ ... })
when your entire structure is dynamic or requires complex JavaScript logic.
JavaScript Functions
Define reusable functions for complex logic, data transformation, and calculations.
Feature: JavaScript functions
Scenario: Simple functions
# Inline function definition
* def greeter = function(name) { return 'Hello ' + name }
* def message = greeter('John')
* match message == 'Hello John'
* def multiply = function(a, b) { return a * b }
* def result = multiply(5, 10)
* match result == 50
Scenario: Multi-line functions
# Use triple quotes for complex logic
* def calculateDiscount =
"""
function(price, customerType) {
if (customerType === 'premium') {
return price * 0.15;
} else if (customerType === 'standard') {
return price * 0.10;
}
return 0;
}
"""
* def discount = calculateDiscount(100, 'premium')
* match discount == 15
Functions with Array Operations
Use ES6 arrow functions and array methods for data transformation.
Feature: Array transformations
Scenario: Filter and map data
* def users =
[
{ name: 'John', age: 30, active: true },
{ name: 'Jane', age: 25, active: false },
{ name: 'Bob', age: 35, active: true }
]
# Filter active users
* def activeUsers = users.filter(u => u.active)
* match activeUsers == '#[2]'
# Extract names
* def names = users.map(u => u.name)
* match names == ['John', 'Jane', 'Bob']
# Chain operations
* def activeNames = users.filter(u => u.active).map(u => u.name)
* match activeNames == ['John', 'Bob']
Karate Utilities
Access built-in functions through the karate
object for common data operations.
Feature: Karate utility functions
Scenario: Common utilities
* def users = [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }]
# Filter with Karate function
* def filtered = karate.filter(users, function(u) { return u.age > 25 })
* match filtered == '#[1]'
# Map with Karate function
* def names = karate.map(users, function(u) { return u.name })
* match names == ['John', 'Jane']
# Array utilities
* def numbers = [1, 2, 2, 3, 3, 3]
* def unique = karate.distinct(numbers)
* match unique == [1, 2, 3]
# Object utilities
* def keys = karate.keysOf(users[0])
* match keys contains 'name'
* match keys contains 'age'
Karate's Graal JS engine supports full ES6. Use native array methods (map
, filter
, reduce
, find
, some
, every
) directly on arrays. The karate
object provides additional utilities like log()
, jsonPath()
, xmlPath()
, distinct()
, and sort()
.
JsonPath Queries
Extract and filter JSON data using JsonPath syntax with wildcards and conditional filters.
Feature: JsonPath operations
Scenario: Basic JsonPath
* def response =
{
store: {
name: 'Tech Store',
books: [
{ title: 'Karate DSL', price: 29.99 },
{ title: 'API Testing', price: 34.99 },
{ title: 'JavaScript', price: 24.99 }
]
}
}
# Simple path extraction
* def storeName = $.store.name
* match storeName == 'Tech Store'
# Array wildcards
* def allTitles = $.store.books[*].title
* match allTitles == ['Karate DSL', 'API Testing', 'JavaScript']
# Array indexing
* def firstBook = $.store.books[0]
* match firstBook.title == 'Karate DSL'
JsonPath Filters
Apply conditional filters to extract specific data from arrays.
Feature: JsonPath filters
Scenario: Filter products
* def catalog =
{
products: [
{ name: 'Laptop', price: 999, rating: 4.5 },
{ name: 'Mouse', price: 29, rating: 4.0 },
{ name: 'Desk', price: 299, rating: 4.2 },
{ name: 'Monitor', price: 399, rating: 4.6 }
]
}
# Filter by price
* def expensive = karate.jsonPath(catalog, "$.products[?(@.price > 200)]")
* match expensive == '#[3]'
# Filter by rating and extract names
* def topRated = karate.jsonPath(catalog, "$.products[?(@.rating >= 4.5)].name")
* match topRated contains 'Laptop'
* match topRated contains 'Monitor'
# Combined conditions
* def expensiveAndRated = karate.jsonPath(catalog, "$.products[?(@.price > 300 && @.rating >= 4.5)]")
* match expensiveAndRated == '#[2]'
XPath Queries
Navigate and query XML documents using XPath syntax with attribute filters and predicates.
Feature: XPath operations
Scenario: Basic XPath
* def order =
"""
<order>
<id>12345</id>
<customer>
<name>John Doe</name>
<email>john@example.com</email>
</customer>
<items>
<item>
<product>Laptop</product>
<price>999.99</price>
</item>
<item>
<product>Mouse</product>
<price>29.99</price>
</item>
</items>
</order>
"""
# Extract values
* def orderId = order/order/id
* def customerName = order/order/customer/name
* match orderId == '12345'
* match customerName == 'John Doe'
# Array access (XPath uses 1-based indexing)
* def firstProduct = order/order/items/item[1]/product
* match firstProduct == 'Laptop'
Advanced XPath
Use attribute filters, predicates, and node counting for complex queries.
Feature: Advanced XPath
Scenario: XPath with attributes and filters
* def inventory =
"""
<inventory>
<product category="electronics" available="true">
<name>Laptop</name>
<price>999</price>
</product>
<product category="electronics" available="false">
<name>Tablet</name>
<price>599</price>
</product>
<product category="books" available="true">
<name>Guide</name>
<price>49</price>
</product>
</inventory>
"""
# Attribute filters
* def available = karate.xmlPath(inventory, '//product[@available="true"]')
* match available == '#[2]'
# Value conditions
* def expensive = karate.xmlPath(inventory, '//product[price > 500]/name')
* match expensive == ['Laptop', 'Tablet']
# Count nodes
* def total = karate.xmlPath(inventory, 'count(//product)')
* match total == 3
XPath uses 1-based indexing ([1]
for first element), unlike JavaScript's 0-based indexing. Use karate.xmlPath()
for complex queries with predicates, attributes, and filters.
Dynamic Patterns
Generate test data and create environment-specific configurations using expressions.
Feature: Dynamic data generation
Scenario: Generate test users
* def generateUser =
"""
function(index) {
return {
id: index,
name: 'User' + index,
email: 'user' + index + '@test.com',
active: index % 2 === 0
};
}
"""
* def users = karate.repeat(3, generateUser)
* match users == '#[3]'
* match users[0].name == 'User0'
* match users[1].email == 'user1@test.com'
Scenario: Environment-based configuration
* def env = karate.env || 'dev'
* def config =
{
baseUrl: '#(env == "prod" ? "https://api.example.com" : "https://api-" + env + ".example.com")',
timeout: '#(env == "prod" ? 60000 : 30000)',
debug: '#(env == "dev")'
}
* match config.timeout > 0
* match config.baseUrl contains env
Next Steps
- Apply expressions in actions: Actions
- Build data-driven tests: Data Driven Tests
- Use expressions in API calls: HTTP Requests
- Master advanced matching: Match Keyword