EXTENSIONS
Test Doubles and Mocking
Create standalone mock servers with stateful behavior for API testing, consumer-driven contracts, and microservice development without external dependencies.
On this page:
- Request matching - Route requests with pathMatches(), methodIs()
- Request variables - Access request, requestHeaders, pathParams
- Response building - Set response, responseStatus, responseDelay
- Starting mocks - karate.start() and MockServer API
- Stateful mocks - Maintain state across requests
- Proxy mode - Forward requests with karate.proceed()
- Server lifecycle - Background vs Scenario execution
- Standalone server - Command-line options and jbang
Request Matching
Matching Predicates
Karate provides helper functions for request matching:
| Function | Description | Example |
|---|---|---|
pathMatches() | Match request paths with path parameters | pathMatches('/users/{id}') |
methodIs() | Check HTTP method (recommended) | methodIs('post') |
paramExists() | Check if query parameter exists | paramExists('name') |
paramValue() | Get single query parameter value | paramValue('limit') |
typeContains() | Match Content-Type header | typeContains('xml') |
acceptContains() | Match Accept header | acceptContains('json') |
headerContains() | Check header contents | headerContains('Authorization', 'Bearer') |
bodyPath() | Query request body using JsonPath/XPath | bodyPath('$.username') |
Request Variables
Available variables for inspecting incoming requests:
| Variable | Description | Example Value |
|---|---|---|
request | Request body (parsed as JSON/XML, or string) | { "name": "John" } |
requestBytes | Raw request body as bytes (for binary content) | [byte array] |
requestMethod | HTTP method in uppercase | GET, POST |
requestPath | Path without query string | /users/1 |
requestUri | Path with query string | /users?page=2 |
requestUrlBase | Protocol, host, and port | http://localhost:8080 |
requestHeaders | All headers (Map of Lists) | { 'Content-Type': ['application/json'] } |
requestParams | Query parameters (Map of Lists) | { 'page': ['2'] } |
requestParts | Multipart parts for file uploads | { 'file': [{ filename: '...' }] } |
pathParams | Path parameters from URL pattern | { 'id': '1' } |
Use helper functions like methodIs('post') and paramValue('page') instead of accessing raw variables directly.
Basic Mock Server
Simple HTTP Mock
Create a basic mock server that responds to requests:
Feature: Simple user mock
Background:
* def users = { '1': { id: 1, name: 'John Doe' } }
Scenario: pathMatches('/users/{id}') && methodIs('get')
* def user = users[pathParams.id]
* def response = user || { error: 'Not found' }
* def responseStatus = user ? 200 : 404
This mock returns predefined user data or a 404 error.
Starting the Mock Server
Start the mock server within a test:
Feature: Test with user service mock
Background:
* def mock = karate.start('user-service-mock.feature')
* url 'http://localhost:' + mock.port
Scenario: Get user by ID
Given path 'users', '1'
When method get
Then status 200
And match response == { id: 1, name: 'John Doe' }
Multiple Endpoints
Add GET and POST endpoints to your mock:
Feature: User service with multiple endpoints
Background:
* def users = { '1': { id: 1, name: 'John Doe', email: 'john@example.com' } }
Scenario: pathMatches('/users/{id}') && methodIs('get')
* def user = users[pathParams.id]
* def response = user || { error: 'User not found' }
* def responseStatus = user ? 200 : 404
Scenario: pathMatches('/users') && methodIs('post')
* def newUser = request
* def newId = (karate.sizeOf(users) + 1) + ''
* def responseStatus = 400
* if (newUser.name && newUser.email) users[newId] = karate.merge(newUser, { id: ~~newId })
* if (newUser.name && newUser.email) response = users[newId]
* if (newUser.name && newUser.email) responseStatus = 201
* if (responseStatus == 400) response = { error: 'Missing required fields' }
Stateful Mocks
Product Catalog Mock
Create a mock with shared state across requests:
Feature: Product catalog mock
Background:
* def products = { '1': { id: 1, name: 'Laptop', price: 999 }, '2': { id: 2, name: 'Mouse', price: 29 } }
Scenario: pathMatches('/products') && methodIs('get')
* def response = karate.valuesOf(products)
Scenario: pathMatches('/products/{id}') && methodIs('get')
* def product = products[pathParams.id]
* def response = product || { error: 'Product not found' }
* def responseStatus = product ? 200 : 404
CRUD Mock
Implement create, read, update, delete operations with shared state:
Feature: CRUD mock
Background:
* def nextId = 1
* def items = {}
Scenario: pathMatches('/items') && methodIs('post')
* def item = request
* item.id = nextId
* items[nextId + ''] = item
* nextId = nextId + 1
* def response = item
* def responseStatus = 201
Scenario: pathMatches('/items/{id}') && methodIs('get')
* def item = items[pathParams.id]
* def response = item || { error: 'Not found' }
* def responseStatus = item ? 200 : 404
Scenario: pathMatches('/items/{id}') && methodIs('delete')
* def item = items[pathParams.id]
* if (item) delete items[pathParams.id]
* def responseStatus = item ? 204 : 404
Catch-All Scenario
Always include a catch-all scenario as the last one to handle unmatched requests. An empty scenario description evaluates to true:
Feature: Mock with catch-all
Background:
* def users = { '1': { id: 1, name: 'John' } }
Scenario: pathMatches('/users/{id}') && methodIs('get')
* def response = users[pathParams.id] || { error: 'Not found' }
* def responseStatus = users[pathParams.id] ? 200 : 404
Scenario:
# Catch-all: returns 404 for any unmatched request
* def response = { error: 'Endpoint not found' }
* def responseStatus = 404
Response Configuration
Status Codes and Headers
Control mock responses using built-in variables:
| Variable | Description | Default |
|---|---|---|
response | Response body (JSON, XML, string, or bytes) | '' |
responseStatus | HTTP status code | 200 |
responseHeaders | Response headers as JSON object | {} |
responseDelay | Delay in milliseconds before responding | 0 |
Feature: Response configuration
Scenario: pathMatches('/api/data')
* def response = { message: 'Created successfully' }
* def responseStatus = 201
* def responseHeaders = { 'X-Request-Id': 'abc123', 'Cache-Control': 'no-cache' }
Simulating Latency
Add realistic network delays to test timeout handling and loading states:
Feature: Delayed responses
Scenario: pathMatches('/slow-api')
* def responseDelay = 2000
* def response = { data: 'delayed response' }
For random delays across all scenarios, use configure afterScenario:
Feature: Random delay mock
Background:
* configure afterScenario = function(){ karate.set('responseDelay', 100 + Math.random() * 500) }
Scenario: pathMatches('/api/data')
* def response = { message: 'Response with random 100-600ms delay' }
Starting Mock Servers
Within a Karate Test
Use karate.start() to launch a mock server from within a feature file. The mock starts on a random available port:
Feature: Test with embedded mock
Background:
* def mock = karate.start('classpath:mocks/user-mock.feature')
* url 'http://localhost:' + mock.port
Scenario: Call mock endpoint
Given path 'users', 1
When method get
Then status 200
And match response.name == '#string'
For more control, pass a JSON configuration:
Feature: Mock with options
Background:
* def mock = karate.start({ mock: 'user-mock.feature', port: 8090 })
* url 'http://localhost:8090'
| Option | Description |
|---|---|
mock | Path to mock feature file (required) |
port | Fixed port number (default: random) |
ssl | Enable HTTPS (default: false) |
cert | SSL certificate path |
key | SSL private key path |
Java API
Embed mocks in JUnit tests using MockServer:
import com.intuit.karate.core.MockServer;
public class MyTest {
static MockServer mockServer;
@BeforeAll
static void setup() {
mockServer = MockServer
.feature("classpath:mocks/user-mock.feature")
.http(0) // Random port
.build();
}
@AfterAll
static void cleanup() {
mockServer.stop();
}
@Test
void testWithMock() {
int port = mockServer.getPort();
// Use port in your tests
}
}
Proxy Mode
Use karate.proceed() to forward requests to real backend services. This enables "AOP for web services" - you can intercept, modify, or conditionally stub requests.
Forwarding Requests
Forward all matching requests to a backend service:
Feature: API proxy
Scenario: pathMatches('/api/users')
* karate.proceed('https://jsonplaceholder.typicode.com')
After karate.proceed() returns, response and responseHeaders contain the backend's response. You can modify them before returning to the client.
Modifying Proxied Responses
Intercept and enrich responses from the backend:
Feature: Response enrichment proxy
Scenario: pathMatches('/api/users/{id}')
* karate.proceed('https://jsonplaceholder.typicode.com')
# Add metadata to response
* response.cached = false
* response.timestamp = new Date().toISOString()
Conditional Stubbing
Stub some requests while forwarding others:
Feature: Selective proxy
Background:
* def backendUrl = 'https://jsonplaceholder.typicode.com'
Scenario: pathMatches('/api/users/99999')
# Stub non-existent user for testing
* def response = { id: 99999, name: 'Test User' }
Scenario: pathMatches('/api/users/{id}')
# Forward all other user requests to real backend
* karate.proceed(backendUrl)
Server Lifecycle
Understanding the mock server lifecycle is essential for building effective mocks.
Background vs Scenario
Unlike normal Karate tests, mock servers have a different execution model:
| Phase | Normal Karate | Mock Server |
|---|---|---|
| Background | Runs before each Scenario | Runs once on startup |
| Scenario | Runs sequentially | Evaluated per incoming request |
Request Matching
For each incoming HTTP request:
- Scenario expressions are evaluated in order from top to bottom
- The first scenario whose expression evaluates to
trueis executed - If no scenario matches, no response is returned (connection timeout)
Place more specific scenarios before general ones. For example, put pathMatches('/users/admin') before pathMatches('/users/{id}').
Using karate.abort()
Stop scenario execution without returning a response:
Feature: Protected endpoint
Scenario: pathMatches('/protected')
* def auth = karate.request.header('authorization')
* if (!auth) karate.abort()
* def response = { message: 'Authorized access' }
Aborted scenarios result in connection timeout for the client - useful for simulating authentication failures.
CORS and Headers
Enable CORS
For browser-based testing, enable CORS with a single line. This automatically adds Access-Control-Allow-Origin: * and related headers:
Feature: Browser-friendly mock
Background:
* configure cors = true
Scenario: pathMatches('/api/data')
* def response = { message: 'CORS enabled' }
Global Response Headers
Set headers that apply to all responses:
Feature: Mock with global headers
Background:
* configure responseHeaders = { 'Content-Type': 'application/json', 'X-Powered-By': 'Karate' }
Scenario: pathMatches('/api/data')
* def response = { message: 'Has global headers' }
Scenario-level responseHeaders override global settings for individual endpoints.
Error Simulation
Test error handling by returning various HTTP error codes:
Feature: Error simulation
Scenario: pathMatches('/api/error/400')
* def response = { error: 'Bad Request', message: 'Invalid input' }
* def responseStatus = 400
Scenario: pathMatches('/api/error/500')
* def response = { error: 'Internal Server Error' }
* def responseStatus = 500
Scenario: pathMatches('/api/timeout')
* def responseDelay = 30000
* def response = { message: 'This will timeout most clients' }
File Uploads
Handle multipart file uploads using requestParts:
Feature: File upload mock
Scenario: pathMatches('/upload') && methodIs('post')
* def filePart = requestParts['file'][0]
* def response =
"""
{
filename: '#(filePart.filename)',
contentType: '#(filePart.contentType)',
size: '#(filePart.value.length)'
}
"""
* def responseStatus = 201
Each part in requestParts contains: name, filename, contentType, charset, transferEncoding, and value (raw bytes).
Standalone Mock Server
Run mocks as standalone servers from the command line. Download the Karate JAR or use jbang.
Using jbang
The easiest way to run mocks without any build setup:
# Install jbang: https://www.jbang.dev
# Run a mock server
jbang karate@karatelabs -m mock.feature -p 8080
# With SSL
jbang karate@karatelabs -m mock.feature -p 8443 -s
# Hot reload during development
jbang karate@karatelabs -m mock.feature -p 8080 -W
Using the JAR
# Basic server
java -jar karate.jar -m mock.feature -p 8080
# Multiple mock files (evaluated in order)
java -jar karate.jar -m users.feature -m orders.feature -p 8080
# With environment variable
java -jar karate.jar -m mock.feature -p 8080 -e staging
Command-Line Options
| Option | Description |
|---|---|
-m | Mock feature file (can specify multiple) |
-p | Port number |
-s | Enable SSL (auto-generates certificate if none provided) |
-c | SSL certificate file (PEM format) |
-k | SSL private key file (PEM format) |
-e | Set karate.env value |
-W | Watch mode - hot reload on file changes |
--keep-original-headers | Preserve header case (for HTTP/1 compatibility) |
Stopping a Running Server
Send a GET request to the admin endpoint to gracefully stop the server:
# Stop HTTP server
curl http://localhost:8080/__admin/stop
# Stop HTTPS server (ignore certificate)
curl -k https://localhost:8443/__admin/stop
Or call mockServer.stop() if using the Java API.
Mock state must use pure JSON objects. Avoid storing Java objects or JavaScript functions with closures - they may cause issues.
The World's Smallest Microservice
This complete CRUD microservice fits in under 300 characters - demonstrating the power of Karate mocks:
Feature:
Background:
* def id = 0
* def m = {}
Scenario: methodIs('post')
* def c = request
* def id = ~~(id + 1)
* c.id = id
* m[id + ''] = c
* def response = c
Scenario: pathMatches('/cats/{id}')
* def response = m[pathParams.id]
Scenario:
* def response = $m.*
This implements POST (create), GET by ID, and GET all for a /cats resource. The ~~ operator converts to integer, and $m.* returns all values from the map.
Next Steps
- Load test your APIs: Performance Testing
- Extend Karate functionality: Actions and Hooks
- Configure parallel test execution: Parallel Execution
- Build reusable test components: Calling Features
- Debug mock servers: Debugging and Troubleshooting