Skip to main content

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
When to Use Each

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'
JavaScript Capabilities

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 Indexing

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