Skip to main content

CORE SYNTAX

Actions and Keywords

Overview

Karate provides essential keywords for HTTP operations and test flow control. Understanding these core actions is fundamental for building effective API tests and managing test execution logic.

Core HTTP Keywords

The five essential HTTP keywords form the foundation of Karate's API testing capabilities:

URL Configuration

# Basic URL setting
* url 'https://api.example.com'
* url baseUrl + '/v1/users'

# Dynamic URL construction
* def environment = karate.env || 'dev'
* def apiUrl = environment == 'prod' ? 'https://api.prod.com' : 'https://api.dev.com'
* url apiUrl

Path Building

# Simple path
* path '/users'

# Multiple path segments
* path 'documents', documentId, 'download'

# Multi-line path building
* path 'api'
* path 'v1'
* path 'users'
* path userId

# Dynamic path construction
* def endpoint = 'users/' + userId + '/profile'
* path endpoint

Request Configuration

# JSON request
* def user = { name: 'John', email: 'john@example.com' }
* request user

# XML request
* request
"""
<user>
<name>John</name>
<email>john@example.com</email>
</user>
"""

# File-based request
* request read('user-payload.json')

# Dynamic request building
* def requestData = { id: userId, action: 'update' }
* request requestData

Method Execution

# Basic HTTP methods
* method get
* method post
* method put
* method delete
* method patch

# Method execution triggers the actual HTTP request
* url 'https://api.example.com'
* path '/users'
* request { name: 'Alice' }
* method post # Request is sent here
* status 201 # Response validation

Status Validation

# Exact status check
* status 200
* status 201
* status 404

# Dynamic status validation
* def expectedStatus = environment == 'dev' ? 201 : 200
* status expectedStatus

# Complex status logic (use responseStatus for conditions)
* assert responseStatus >= 200 && responseStatus < 300

Request Setup Keywords

Parameters

# Query parameters
* param q = 'search term'
* param limit = 10
* param offset = 0

# Multiple parameters at once
* params { q: 'karate', type: 'framework', active: true }

# Dynamic parameters
* def searchParams = { query: searchTerm, page: currentPage }
* params searchParams

Headers

# Individual headers
* header Accept = 'application/json'
* header Content-Type = 'application/xml'
* header Authorization = 'Bearer ' + authToken

# Multiple headers
* headers { Accept: 'application/json', 'User-Agent': 'Karate' }

# Dynamic headers
* def commonHeaders = { 'X-API-Version': '2.0', 'X-Client': 'karate' }
* headers commonHeaders

Cookies

# Individual cookies
* cookie sessionId = '12345'
* cookie preferences = 'theme=dark'

# Multiple cookies
* cookies { sessionId: 'abc123', userId: '456' }

# Cookies from previous responses are auto-included
# Clear cookies if needed
* configure cookies = null

Form Data

# Form fields
* form field username = 'testuser'
* form field password = 'secret'

# Multiple form fields
* form fields { username: 'admin', password: 'admin123', remember: true }

# File upload with multipart
* multipart field file = { read: 'test-data.json', filename: 'data.json', contentType: 'application/json' }
* multipart field description = 'Test file upload'

Configuration Management

Global Configuration

# HTTP client configuration
* configure connectTimeout = 5000
* configure readTimeout = 30000
* configure ssl = true
* configure followRedirects = false

# Headers for all requests
* configure headers = { 'Accept': 'application/json' }

# Logging configuration
* configure logPrettyRequest = true
* configure logPrettyResponse = true
* configure printEnabled = false

Dynamic Headers Configuration

# Function-based header configuration
* def getHeaders =
"""
function() {
var authToken = karate.get('authToken');
return {
'Authorization': 'Bearer ' + authToken,
'X-Request-ID': java.util.UUID.randomUUID()
};
}
"""
* configure headers = getHeaders

Common Configuration Options

KeyTypeDescription
urlstringBase URL for requests
headersJSON/FunctionDefault headers for all requests
sslboolean/stringSSL/TLS configuration
connectTimeoutintegerConnection timeout (milliseconds)
readTimeoutintegerRead timeout (milliseconds)
retryJSONRetry configuration { count: 3, interval: 3000 }
proxystring/JSONHTTP proxy configuration
charsetstringCharacter encoding (default: utf-8)
followRedirectsbooleanAuto-follow redirects (default: true)

Retry Configuration

# Configure retry behavior
* configure retry = { count: 5, interval: 2000 }

# Retry until condition is met
* retry until responseStatus == 200
* url 'https://api.example.com/status'
* method get

# Retry with custom condition
* retry until response.status == 'ready' && response.count > 5
* url 'https://api.example.com/job/' + jobId
* method get

Conditional Logic and Flow Control

Conditional Execution

# Simple conditional
* def filename = zone == 'zone1' ? 'test1.feature' : 'test2.feature'
* def result = call read(filename)

# Conditional feature calls
* def result = responseStatus == 404 ? {} : karate.call('delete-user.feature')

# Conditional logic with karate.call()
* if (responseStatus == 200) karate.call('cleanup.feature')

The eval Keyword

# Simple evaluation
* eval myJavaScriptFunction()

# Conditional evaluation
* eval if (zone == 'zone1') karate.set('temp', 'after')

# Complex JavaScript logic
* eval
"""
var calculateDiscount = function(price, type) {
if (type === 'premium') return price * 0.8;
if (type === 'regular') return price * 0.9;
return price;
};
var finalPrice = calculateDiscount(basePrice, customerType);
karate.set('discountedPrice', finalPrice);
"""

Flow Control Patterns

# Early exit pattern
* if (skipTest) karate.abort()

# Environment-specific logic
* def shouldRunCleanup = environment != 'prod'
* if (shouldRunCleanup) call read('cleanup.feature')

# Data-driven conditionals
* def testData = responseStatus == 200 ? response : fallbackData
* def nextStep = testData.success ? 'success.feature' : 'error.feature'
* call read(nextStep)

Error Handling

# Graceful failure handling
* if (response.error) karate.fail('API returned error: ' + response.error.message)

# Conditional assertions
* if (response.items) assert response.items.length > 0

# Environment-specific validation
* if (environment == 'prod') assert response.securityToken != null

Advanced Action Patterns

Request-Response Chaining

# Get authentication token
* url authUrl
* request { username: 'admin', password: 'secret' }
* method post
* status 200
* def authToken = response.token

# Use token in subsequent requests
* url apiUrl
* header Authorization = 'Bearer ' + authToken
* path '/users'
* method get
* status 200

Dynamic Configuration

# Configure based on environment
* def config = karate.env == 'prod' ? prodConfig : devConfig
* configure connectTimeout = config.timeout
* configure headers = config.headers
* url config.baseUrl

Polling and Waiting

# Poll until ready
* configure retry = { count: 10, interval: 1000 }
* retry until response.status == 'completed'
* url apiUrl + '/job/' + jobId
* method get

# Wait for async operation
* def pollJob =
"""
function() {
karate.call('get-job-status.feature', { jobId: jobId });
return karate.get('response.status') === 'completed';
}
"""
* eval
"""
var maxAttempts = 30;
var attempt = 0;
while (attempt < maxAttempts && !pollJob()) {
java.lang.Thread.sleep(2000);
attempt++;
}
if (attempt >= maxAttempts) {
karate.fail('Job did not complete within timeout');
}
"""

Multi-Step Workflows

# Create resource
* url baseUrl
* path '/users'
* request { name: 'Test User', email: 'test@example.com' }
* method post
* status 201
* def userId = response.id

# Update resource
* path '/users', userId
* request { email: 'updated@example.com' }
* method put
* status 200

# Verify update
* path '/users', userId
* method get
* status 200
* match response.email == 'updated@example.com'

# Cleanup
* method delete
* status 204

Image Comparison

The compareImage Action

Karate provides powerful image comparison capabilities for visual testing, especially useful for UI testing scenarios where screenshots need validation against baseline images.

Basic Usage

# Simple comparison
* compareImage { baseline: 'screenshots/login.png', latest: '/tmp/login.png' }

# Using path prefixes
* compareImage { baseline: 'classpath:screenshots/login.png', latest: 'file:/tmp/login.png' }

# Using byte arrays
* def latestImgBytes = karate.readAsBytes('login.png')
* compareImage { baseline: 'screenshots/login.png', latest: '#(latestImgBytes)' }

Configuration Options

Configure image comparison behavior globally or per comparison:

# Global configuration
* configure imageComparison = { failureThreshold: 2, engine: 'ssim' }

# Per-comparison options
* compareImage { baseline: 'login.png', latest: 'new.png', options: { ignore: 'antialiasing' } }
Main Configuration Keys
KeyTypeDefaultDescription
allowScalingbooleanfalseScale images to match baseline dimensions
enginestring'resemble'Comparison engine: 'resemble', 'ssim', or both
failureThresholdnumber0.0Percentage of pixels allowed to differ
mismatchShouldPassbooleanfalsePass even when differences exceed threshold
hideUiOnSuccessbooleanfalseHide comparison UI in reports for passing comparisons
onShowRebaseJS functionnullCustom rebase handler for reports
onShowConfigJS functionnullCustom config display handler

Engine Selection

# Use only SSIM (Structural Similarity)
* configure imageComparison = { engine: 'ssim' }

# Use both engines, evaluate lowest mismatch
* configure imageComparison = { engine: 'resemble,ssim' }

# Fallback pattern: try resemble, then ssim if threshold exceeded
* configure imageComparison = { engine: 'resemble|ssim' }

Ignoring Regions

# Ignore specific areas (e.g., dynamic content)
* def ignoredBoxes =
"""
[{
top: 100,
left: 200,
bottom: 150,
right: 300
}]
"""
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { ignoredBoxes: '#(ignoredBoxes)' }
}

# Ignore areas with specific color
* def purple = { r: 190, g: 0, b: 255 }
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { ignoreAreasColoredWith: '#(purple)' }
}

Advanced Options

Resemble Engine Options
# Grayscale comparison
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { ignoreColors: true }
}

# Antialiasing detection
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { ignoreAntialiasing: true }
}

# Custom tolerances
* def tolerances = { red: 4, green: 4, blue: 4, alpha: 4 }
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { tolerances: '#(tolerances)' }
}
SSIM Engine Options
# Fast algorithm
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { ssim: 'FAST' }
}

# Custom window size
* compareImage {
baseline: 'baseline.png',
latest: 'latest.png',
options: { windowSize: 7 }
}

Report Integration

# Custom rebase handler
* text rebaseFn =
"""
function (config, downloadLatestFn) {
downloadLatestFn('custom_baseline.png');
return 'Baseline updated successfully';
}
"""
* configure imageComparison = { onShowRebase: '#(rebaseFn)' }

# Hide successful comparisons in reports
* configure imageComparison = { hideUiOnSuccess: true }

Integration with UI Testing

Combine with browser automation for visual regression testing:

Scenario: Visual regression test
* driver 'https://example.com/login'
* screenshot()
* def screenshot = karate.readAsBytes('screenshot.png')
* compareImage {
baseline: 'classpath:baselines/login.png',
latest: '#(screenshot)',
options: { failureThreshold: 1 }
}

Note: For more UI testing capabilities, see UI Testing.

The doc Keyword

HTML Template Rendering

Embed HTML templates and custom content in test reports using the doc keyword:

# Basic HTML template from file
* doc { read: 'report-template.html' }

# Inline HTML with variables
* def user = { name: 'John', id: 123 }
* doc
"""
<div>
<h3>User Details</h3>
<p>Name: ${user.name}</p>
<p>ID: ${user.id}</p>
</div>
"""

Dynamic Report Generation

# Generate custom test summary
* def results = call read('test-suite.feature')
* def template = read('classpath:templates/results.html')
* doc karate.render(template, results)

# Create data tables
* def users = [{name: 'Alice', score: 95}, {name: 'Bob', score: 87}]
* doc
"""
<table>
<tr><th>Name</th><th>Score</th></tr>
#(users.map(u => '<tr><td>' + u.name + '</td><td>' + u.score + '</td></tr>').join(''))
</table>
"""

Use Cases

The doc keyword is particularly useful for:

  • Custom test summaries and dashboards
  • Embedding additional context in reports
  • Creating formatted test documentation
  • Displaying complex data structures visually
  • Adding charts or graphs (when combined with JavaScript libraries)

Tip: Use doc with templating for rich HTML reports. See Parallel Execution for report generation details.

Advanced eval Usage

JavaScript Execution Patterns

While eval is primarily for JavaScript execution, it offers advanced patterns beyond simple expressions:

# Multi-line JavaScript without 'eval' keyword
*
"""
var data = { id: 123 };
data.timestamp = new Date().toISOString();
data.random = Math.random();
karate.set('processedData', data);
"""

# Conditional data manipulation
* eval
"""
if (response.status === 'pending') {
karate.call('wait-for-completion.feature');
} else if (response.status === 'error') {
karate.fail('Process failed: ' + response.error);
}
"""

Complex Transformations

# Array processing with modern JavaScript
* def transform =
"""
function(data) {
return data
.filter(item => item.active)
.map(item => ({
...item,
processed: true,
timestamp: Date.now()
}))
.sort((a, b) => b.timestamp - a.timestamp);
}
"""
* eval karate.set('result', transform(response.items))

# Dynamic feature calls
* eval
"""
var features = ['user', 'admin', 'guest'];
features.forEach(function(role) {
var result = karate.call('test-' + role + '.feature');
karate.log(role + ' test:', result.passed ? 'PASSED' : 'FAILED');
});
"""

Java Integration

# Using Java classes
* eval
"""
var UUID = Java.type('java.util.UUID');
var LocalDateTime = Java.type('java.time.LocalDateTime');
karate.set('id', UUID.randomUUID().toString());
karate.set('timestamp', LocalDateTime.now().toString());
"""

# Custom Java utilities
* eval
"""
var Utils = Java.type('com.mycompany.TestUtils');
var encrypted = Utils.encrypt(request.password);
karate.set('request', {...request, password: encrypted});
"""

When to Use eval

  • ✅ Complex JavaScript logic that doesn't fit in expressions
  • ✅ Multi-statement operations
  • ✅ Integration with Java classes
  • ✅ Dynamic feature calls based on conditions
  • ❌ Simple variable assignments (use def instead)
  • ❌ Basic conditionals (use if statements)

Warning: Overuse of eval can make tests harder to read. Prefer Karate's built-in keywords when possible. For conditional logic patterns, see Conditional Logic.

Best Practices

Action Organization

# ✅ Good: Group related configurations
Background:
* url baseUrl
* configure headers = { 'Accept': 'application/json' }
* configure connectTimeout = 5000

# ✅ Good: Clear request setup
Scenario: Create user
Given path '/users'
And request { name: 'John', email: 'john@example.com' }
When method post
Then status 201
And match response.id == '#string'

Error Prevention

# ✅ Good: Validate prerequisites
* if (!baseUrl) karate.fail('baseUrl must be configured')
* if (!authToken) karate.fail('Authentication required')

# ✅ Good: Defensive programming
* def timeout = karate.get('customTimeout', 5000)
* configure connectTimeout = timeout

Configuration Management

# ✅ Good: Environment-specific setup
Background:
* def config = call read('config.feature')
* url config.baseUrl
* configure headers = config.headers
* configure ssl = config.sslEnabled

# ✅ Good: Reusable configuration functions
* def setupAuth =
"""
function() {
if (!karate.get('authToken')) {
karate.call('authenticate.feature');
}
return { 'Authorization': 'Bearer ' + karate.get('authToken') };
}
"""
* configure headers = setupAuth

Performance Optimization

# ✅ Good: Optimize timeouts
* configure connectTimeout = environment == 'local' ? 1000 : 5000
* configure readTimeout = environment == 'local' ? 5000 : 30000

# ✅ Good: Reuse expensive operations
Background:
* def authData = callonce read('authenticate.feature')
* def authToken = authData.token

Troubleshooting

Common Issues

# Debug configuration
* print 'Current URL:', karate.get('url')
* print 'Headers:', karate.get('headers')
* print 'Environment:', karate.env

# Validate request setup
* print 'Request body:', request
* print 'Method:', method
* print 'Full URL:', url + path

Configuration Debugging

# Check active configuration
* def currentConfig =
"""
{
url: karate.get('url'),
headers: karate.get('headers'),
connectTimeout: karate.get('connectTimeout'),
ssl: karate.get('ssl')
}
"""
* print 'Active config:', currentConfig

Next Steps