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
Key | Type | Description |
---|---|---|
url | string | Base URL for requests |
headers | JSON/Function | Default headers for all requests |
ssl | boolean/string | SSL/TLS configuration |
connectTimeout | integer | Connection timeout (milliseconds) |
readTimeout | integer | Read timeout (milliseconds) |
retry | JSON | Retry configuration { count: 3, interval: 3000 } |
proxy | string/JSON | HTTP proxy configuration |
charset | string | Character encoding (default: utf-8) |
followRedirects | boolean | Auto-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
Key | Type | Default | Description |
---|---|---|---|
allowScaling | boolean | false | Scale images to match baseline dimensions |
engine | string | 'resemble' | Comparison engine: 'resemble', 'ssim', or both |
failureThreshold | number | 0.0 | Percentage of pixels allowed to differ |
mismatchShouldPass | boolean | false | Pass even when differences exceed threshold |
hideUiOnSuccess | boolean | false | Hide comparison UI in reports for passing comparisons |
onShowRebase | JS function | null | Custom rebase handler for reports |
onShowConfig | JS function | null | Custom 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
- Learn about HTTP Requests for comprehensive API testing
- Explore HTTP Responses for response validation
- Understand Assertions for powerful validation patterns