API REFERENCE
Karate Keywords Reference
Complete reference of all Karate DSL keywords, actions, and the karate object.
On this page:
- Feature Structure - Feature, Scenario, Background, Scenario Outline
- Core Keywords - def, match, call, read, get, set, remove, replace, copy
- HTTP Keywords - url, path, method, request, headers, params, cookies
- Response Variables - response, responseStatus, responseHeaders, etc.
- Assertions - match variants, assert, print
- Type Conversion - json, xml, string, bytes, yaml, csv
- Control Flow - if, retry until, eval, listen
- Configuration - All configure options
- The karate Object - All karate.* methods and properties
- Magic Variables - __arg, __loop, __row, __num, listenResult
- Mock Server - pathMatches, methodIs, request variables
- Tags - @ignore, @setup, @lock, @env, @report
Feature Structure
Every Karate test file must have a .feature extension and follow this structure.
Feature
The Feature: declaration is required at the top of every feature file:
Feature: User API Tests
Scenario: Get user
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', 1
When method get
Then status 200
Background
Runs before each Scenario in the feature. Use for shared setup:
Feature: User API
Background:
* url 'https://jsonplaceholder.typicode.com'
* def authToken = 'Bearer abc123'
Scenario: Get user
* path 'users', 1
* method get
* status 200
Scenario: Get posts
* path 'posts', 1
* method get
* status 200
Scenario
Defines a single test case:
Feature: User tests
Scenario: Create and verify user
Given url 'https://jsonplaceholder.typicode.com'
And path 'users'
And request { name: 'John', username: 'john' }
When method post
Then status 201
And match response.name == 'John'
Scenario Outline
Data-driven tests with multiple examples. Use <placeholder> syntax or direct variable references:
Feature: Data-driven tests
Scenario Outline: Get users by ID
Given url 'https://jsonplaceholder.typicode.com'
And path 'users', <id>
When method get
Then status 200
And match response.name == '<name>'
Examples:
| id | name |
| 1 | Leanne Graham |
| 2 | Ervin Howell |
| 3 | Clementine Bauch |
Core Keywords
def
Define variables of any type:
* def name = 'John'
* def age = 30
* def active = true
* def user = { name: 'John', age: 30 }
* def items = [1, 2, 3]
* def result = call read('helper.feature')
match
Assert and validate data. The primary assertion keyword:
* match response == { id: 1, name: 'John' }
* match response.id == 1
* match response contains { name: 'John' }
* match response.items == '#[]'
* match each response.users == { id: '#number', name: '#string' }
See Assertions for all match variants.
call
Call another feature file or JavaScript function:
* def result = call read('auth.feature')
* def result = call read('auth.feature') { username: 'john' }
* def token = call read('auth.feature@login')
* def result = call read('flows/' + flowName + '.feature')
* def computed = call myFunction
The path passed to read() can be any expression — including string concatenation with variables, as in the dynamic example above.
callonce
Call a feature file only once per feature (cached across scenarios):
* def auth = callonce read('auth.feature')
* callonce read('setup.feature')
read
Read files from classpath or filesystem:
* def data = read('data.json')
* def users = read('classpath:data/users.csv')
* def template = read('template.xml')
* def config = read('this:config.json')
| Prefix | Description |
|---|---|
classpath: | From classpath (src/test/resources) |
file: | Absolute file path |
this: | Relative to current feature file |
Content is auto-converted by extension: JSON, XML, CSV and YAML are parsed, JS is evaluated, feature files become callable, and binary extensions (pdf, png, jpg, jpeg, gif, ico, mp4, bin, zip, gz, tar, xlsx, xls, docx, doc, pptx, ppt) return byte arrays. Other extensions return a string — unless the content starts with a zip, gzip, or OLE2 binary signature, in which case bytes are returned.
get
Extract values using JsonPath or XPath:
* def name = get response.user.name
* def first = get[0] response.users
* def ids = get response..id
* def adults = get users[?(@.age > 18)]
Short-cut forms:
* def name = response.user.name
* def first = response.users[0]
set
Set nested values in JSON or XML:
* set user.name = 'John'
* set user.address.city = 'Boston'
* set items[0].active = true
* set response.items[*].processed = true
Multi-value form:
* set user
| path | value |
| name | 'John' |
| age | 30 |
setFor a single direct key or index, plain JS assignment is shorter and does the same thing — it mutates the underlying map in place:
* user.name = 'John'
* user['hy-phen'] = 'value' # bracket notation handles keys with dashes, dots, spaces
* items[0].active = true
Reach for set when you need something JS assignment can't express:
- JSONPath wildcards or recursive descent —
set foo.items[*].active = true,set foo..debug = false - Auto-vivification of missing intermediate objects —
set foo.a.b.c = 1whenfoo.adoesn't yet exist - XML xpath updates —
set search /acc:phoneNumber = 1234 - The
| path | value |table form for bulk writes
remove
Remove keys from JSON or nodes from XML:
* remove response.password
* remove response..internal
* remove response.users[0]
replace
Text replacement in strings:
* replace text.'{name}' = 'John'
* replace text.'{id}' = id
Multi-value form:
* replace text
| token | value |
| {name} | John |
| {id} | 123 |
copy
Deep copy a variable (prevents mutation):
* copy user = response.user
* set user.name = 'Modified'
# response.user.name is unchanged
table
Define inline data tables:
* table users
| name | age |
| John | 30 |
| Jane | 25 |
* match users == [{ name: 'John', age: 30 }, { name: 'Jane', age: 25 }]
text
Multi-line text (preserves formatting):
* text query =
"""
query GetUser($id: ID!) {
user(id: $id) {
name
email
}
}
"""
doc
Add documentation to HTML reports:
* doc 'This scenario tests the user creation flow'
listen
Wait for async events with timeout (milliseconds):
* listen 5000
* match listenResult == { type: 'notification' }
HTTP Keywords
url
Set the base URL:
* url 'https://jsonplaceholder.typicode.com'
* url baseUrl
path
Append path segments to URL:
* path 'users'
* path 'users', userId
* path 'users', 1, 'posts'
method
Execute HTTP request:
* method get
* method post
* method put
* method patch
* method delete
* method head
* method options
The verb can also come from a variable or an expression. A standard verb is always taken literally, so a variable can never shadow get:
* def httpMethod = 'post'
* method httpMethod
request
Set request body:
* request { name: 'John', email: 'john@test.com' }
* request read('request.json')
* request ''
header / headers
Set request headers:
* header Accept = 'application/json'
* header Authorization = 'Bearer ' + token
* headers { 'Content-Type': 'application/json', 'X-Api-Key': apiKey }
param / params
Set query parameters:
* param page = 1
* param size = 10
* params { page: 1, size: 10, sort: 'name' }
cookie / cookies
Set cookies:
* cookie session = 'abc123'
* cookies { session: 'abc123', userId: '456' }
form field / form fields
Set form data (application/x-www-form-urlencoded):
* form field username = 'john'
* form field password = 'secret'
* form fields { username: 'john', password: 'secret' }
multipart field / multipart fields
Set multipart form fields:
* multipart field name = 'John'
* multipart field file = { read: 'test.pdf', filename: 'test.pdf' }
* multipart fields { name: 'John', email: 'john@test.com' }
multipart file / multipart files
Upload files:
* multipart file myFile = { read: 'test.pdf', filename: 'test.pdf', contentType: 'application/pdf' }
* multipart files { file1: { read: 'a.pdf' }, file2: { read: 'b.pdf' } }
multipart entity
Set entire multipart body with custom content type:
* multipart entity read('data.json')
soap action
Set SOAP action header and execute POST:
* soap action 'http://tempuri.org/GetUser'
status
Assert response status code:
* status 200
* status 201
* status 404
Response Variables
These are automatically set after each HTTP request.
| Variable | Type | Description |
|---|---|---|
response | any | Response body (auto-parsed as JSON/XML) |
responseStatus | number | HTTP status code |
responseStatusText | string | HTTP status reason phrase (e.g. OK, Not Found) |
responseHeaders | object | Response headers (values are arrays) |
responseCookies | object | Response cookies |
responseTime | number | Response time in milliseconds |
responseBytes | byte[] | Raw response as byte array |
responseType | string | Response content type |
requestTimeStamp | number | Request timestamp (epoch ms) |
* method get
* match responseStatus == 200
* match responseHeaders['Content-Type'][0] contains 'json'
* assert responseTime < 1000
* def bytes = responseBytes
prevRequest
Inspect the actual HTTP request that was sent:
* method post
* print prevRequest.uri
* print prevRequest.method
* print prevRequest.headers
* print prevRequest.body
karate.response
Inspect the previous HTTP response. Provides status, body, headers, and a header(name) helper for case-insensitive single-value lookup:
* method get
* match karate.response.status == 200
* match karate.response.header('content-type') contains 'application/json'
* def body = karate.response.body
For simple header assertions, prefer match header content-type contains 'json' — already case-insensitive.
Assertions
match ==
Exact equality:
* match response == { id: 1, name: 'John' }
* match response.id == 1
match !=
Not equals:
* match response.status != 'error'
* match response != { error: true }
match contains
Partial match (subset):
* match response contains { name: 'John' }
* match response.items contains { id: 1 }
match !contains
Does not contain:
* match response !contains { error: true }
* match response.roles !contains 'admin'
match contains only
Contains exactly these items (any order):
* match response.items contains only [{ id: 2 }, { id: 1 }]
match contains any
Contains at least one of:
* match response.tags contains any ['urgent', 'high']
match contains deep
Deep partial match (recursive):
* match response contains deep { user: { name: 'John' } }
match contains only deep
Contains exactly these items with deep comparison (any order):
* match response.items contains only deep [{ id: 1, data: { x: 1 } }]
match each
Validate each element in array:
* match each response.users == { id: '#number', name: '#string' }
* match each response.users contains { active: true }
match each contains deep
Each element contains (deep):
* match each response.orders contains deep { items: [{ price: '#number' }] }
match header
Match response header:
* match header Content-Type contains 'json'
assert
Boolean assertions (use for comparisons like >, <):
* assert response.count > 0
* assert response.items.length >= 10
* assert responseTime < 1000
print
Debug output (appears in console and reports):
* print response
* print 'Status:', responseStatus
* print karate.pretty(response)
Type Conversion
json
Convert to JSON — from a string, an XML variable, a Java object, or a doc string:
* json data = '{"name":"John"}'
* json data = xmlResponse
* json data =
"""
{ name: 'John' }
"""
A JSON or XML literal typed at the step (including a doc string) is a template, so its #(...) expressions are substituted. A string parsed into JSON is not — it is kept exactly as authored. That makes json plus karate.readAsString() the way to load a schema file whose placeholders must survive until the match — see Cross-Referenced Schema Files.
xml
Convert to XML:
* xml data = '<user><name>John</name></user>'
* xml data = jsonResponse
string
Convert to string:
* string jsonText = { name: 'John' }
* match jsonText == '{"name":"John"}'
xmlstring
Convert XML to string:
* xmlstring text = xmlData
bytes
Convert to byte array:
* bytes data = read('file.pdf')
yaml
Parse YAML to JSON:
* yaml data =
"""
name: John
age: 30
"""
* match data == { name: 'John', age: 30 }
csv
Read CSV as JSON array:
* def users = read('users.csv')
* match users[0] == { name: 'John', age: '30' }
Control Flow
if
Conditional execution:
* if (responseStatus == 200) karate.call('success.feature')
* if (env == 'dev') karate.set('baseUrl', devUrl)
eval
Execute JavaScript:
* eval response.items.forEach(x => x.processed = true)
* eval if (condition) karate.set('flag', true)
retry until
Retry request until condition is met:
* configure retry = { count: 10, interval: 1000 }
* Given url 'https://jsonplaceholder.typicode.com'
* And path 'users', 1
* And retry until response.status == 'complete'
* When method get
The retry until expression must be pure JavaScript. Karate match syntax will not work.
Configuration Options
Use configure to set options. Can be done in karate-config.js or feature files.
HTTP Configuration
| Option | Type | Default | Description |
|---|---|---|---|
url | string | - | Base URL for all requests |
ssl | boolean/JSON | false | Enable SSL/TLS |
connectTimeout | number | 30000 | Connection timeout (ms) |
readTimeout | number | 30000 | Read timeout (ms) |
proxy | string/JSON | - | HTTP proxy settings. Supports nonProxyHosts with wildcards |
followRedirects | boolean | true | Follow HTTP redirects |
strictResponseParsing | boolean | false | Parse the response per its declared Content-Type only (no content sniffing) |
charset | string | 'utf-8' | Request charset |
headers | JSON/function | - | Default headers for all requests |
cookies | JSON | - | Default cookies (null to clear) |
localAddress | string | - | Local network interface |
httpRetryEnabled | boolean | false | Auto-retry on connection errors |
* configure ssl = true
* configure connectTimeout = 60000
* configure headers = { 'X-Api-Key': '#(apiKey)' }
* configure proxy = { uri: 'http://proxy:8080', username: 'user', password: 'pass' }
* configure proxy = { uri: 'http://proxy:8080', nonProxyHosts: ['localhost', '*.internal.com'] }
SSL Configuration
* configure ssl = { keyStore: 'classpath:certs.pfx', keyStorePassword: 'secret', keyStoreType: 'pkcs12' }
* configure ssl = { trustAll: true }
| Option | Description |
|---|---|
keyStore | Path to client certificate |
keyStorePassword | Certificate password |
keyStoreType | Format (pkcs12, jks) |
trustStore | Path to trust store |
trustStorePassword | Trust store password |
trustAll | Trust all certificates |
algorithm | SSL algorithm (TLS, TLSv1.2) |
Logging and Reports
| Option | Type | Default | Description |
|---|---|---|---|
logging | JSON | { report: 'debug', console: 'info', pretty: true } | Unified logging — thresholds, pretty body formatting, declarative HTTP mask. See Logging. |
* configure logging = { report: 'debug', console: 'info', pretty: true }
* configure logging = { mask: { headers: ['Authorization'], jsonPaths: ['$.password', '$..token'] } }
* configure logging = { mask: { patterns: [{ regex: 'Bearer [A-Za-z0-9._-]+', replacement: 'Bearer ***' }] } }
To hide an entire scenario from HTML / Cucumber JSON / JUnit XML / JSONL artifacts (the run still happens and counts toward suite totals), tag it @report=false.
Retry Configuration
* configure retry = { count: 3, interval: 5000 }
Hooks
| Option | Description |
|---|---|
beforeScenario | Run before each Scenario, before Background (set in karate-config.js) |
afterScenario | Run after each Scenario (pass or fail) |
afterScenarioOutline | Run after Scenario Outline completes |
afterFeature | Run after Feature completes |
* configure afterScenario = function(){ karate.log('Scenario done:', karate.scenario.name) }
Hook exceptions fail the enclosing scenario by default — wrap the hook body in try/catch to suppress. See Hooks and Lifecycle for details.
Mock Server
| Option | Description |
|---|---|
cors | Enable CORS (boolean) |
responseHeaders | Default response headers |
Other Options
| Option | Type | Default | Description |
|---|---|---|---|
callSingleCache | JSON | {minutes:0} | Cache for callSingle |
abortedStepsShouldPass | boolean | false | Mark aborted steps as passed |
abortSuiteOnFailure | boolean | false | Stop on first failure, interrupts in-flight parallel scenarios |
xmlNamespaceAware | boolean | false | XML namespace handling |
matchEachEmptyAllowed | boolean | false | Allow empty arrays in match each |
pauseIfNotPerf | boolean | false | Pause in non-perf mode |
ntlmAuth | JSON | - | NTLM authentication |
driver | JSON | - | UI automation driver |
driverTarget | JSON | - | Driver target config |
The karate Object
The karate object provides utility methods and runtime information.
Properties
| Property | Description |
|---|---|
karate.env | Current environment (karate.env system property) |
karate.properties | Access Java system properties — prefer karate.sysprop() for a single value |
karate.os | OS info: { type: 'macosx', name: 'Mac OS X' } |
karate.feature | Current feature metadata |
karate.scenario | Current scenario metadata |
karate.scenarioOutline | Scenario outline metadata |
karate.tags | Current scenario tags |
karate.tagValues | Tag values for @name=value format |
karate.prevRequest | Last HTTP request details |
karate.info | Runtime information |
karate.config | Effective runtime configuration |
karate.suite | Suite-level info: threadCount, dryRun |
* print 'Environment:', karate.env
* def port = karate.properties['server.port']
* def isWindows = karate.os.type == 'windows'
* print 'Scenario:', karate.scenario.name
Core Methods
| Method | Description |
|---|---|
karate.call(feature, [arg]) | Call feature file |
karate.callSingle(feature, [arg]) | Call once and cache |
karate.read(path) | Read file |
karate.readAsString(path) | Read file as string (no auto-convert) |
karate.readAsBytes(path) | Read file as byte array |
karate.readAsStream(path) | Read file as InputStream |
karate.write(object, path) | Write to file (in target/) |
karate.get(name, [default]) | Get variable value |
karate.set(name, value) | Set variable value |
karate.set(name, path, value) | Set nested value |
karate.set(object) | Set multiple from map |
karate.remove(name, path) | Remove from JSON/XML |
karate.setXml(name, xmlString) | Set XML value |
karate.sysenv(name, [default]) | Read an OS environment variable; falls back to default when unset or empty |
karate.sysprop(name, [default]) | Read a JVM system property; cleaner alternative to karate.properties[name] with default support |
* def result = karate.call('helper.feature')
* def config = karate.callSingle('classpath:setup.feature')
* def foo = karate.get('foo', 'default')
* karate.set('myVar', 123)
* def host = karate.sysenv('API_HOST', 'localhost')
* def port = karate.sysprop('server.port', '8080')
JSON/XML Methods
| Method | Description |
|---|---|
karate.jsonPath(json, expr) | Evaluate JsonPath |
karate.xmlPath(xml, expr) | Evaluate XPath |
karate.pretty(json) | Pretty print JSON |
karate.prettyXml(xml) | Pretty print XML |
karate.fromString(string) | Auto-detect and parse JSON/XML |
karate.toJson(object, [removeNulls]) | Convert to JSON |
karate.lowerCase(object) | Lowercase all keys/values |
* def names = karate.jsonPath(response, '$.users[*].name')
* def title = karate.xmlPath(doc, '//book/title')
* print karate.pretty(response)
Collection Methods
| Method | Description |
|---|---|
karate.map(list, fn) | Transform each element |
karate.filter(list, fn) | Filter elements |
karate.forEach(list, fn) | Iterate with side effects |
karate.filterKeys(map, keys) | Extract subset of keys |
karate.merge(...maps) | Merge objects |
karate.append(...items) | Combine arrays/objects |
karate.appendTo(name, ...items) | Append to existing array |
karate.mapWithKey(list, key) | Transform primitives to objects |
karate.sort(list, [fn]) | Sort array |
karate.distinct(list) | Remove duplicates |
karate.repeat(count, fn) | Execute n times |
karate.range(start, end, [step]) | Generate number range |
karate.keysOf(object) | Get object keys |
karate.valuesOf(object) | Get object values |
karate.sizeOf(object) | Get size/length |
karate.typeOf(value) | Get type name |
* def ids = karate.map(response.users, x => x.id)
* def active = karate.filter(response.users, x => x.active)
* def merged = karate.merge(defaults, overrides)
* def items = karate.repeat(5, i => ({ id: i }))
String Methods
| Method | Description |
|---|---|
karate.extract(text, regex, group) | Extract regex match |
karate.extractAll(text, regex, [group]) | Extract all matches |
karate.urlEncode(string) | URL encode |
karate.urlDecode(string) | URL decode |
* def id = karate.extract(text, 'id=(\\d+)', 1)
* def numbers = karate.extractAll(text, '\\d+')
* def encoded = karate.urlEncode('hello world')
Random/Utility Methods
| Method | Description |
|---|---|
karate.uuid() | Generate UUID |
karate.random([max]) | Random number (0 to max-1) |
karate.now() | Current timestamp (epoch ms) |
karate.match(actual, expected) | Fuzzy match (returns { pass, message }) |
* def id = karate.uuid()
* def num = karate.random(100)
* def time = karate.now()
Type Conversion Methods
| Method | Description |
|---|---|
karate.toBean(json, className) | JSON to Java object |
karate.toJava(function) | JS function to Java |
karate.toJavaFile(path) | Get java.io.File |
karate.toMap(object) | Convert to Java Map |
karate.toCsv(list) | JSON array to CSV string |
karate.toAbsolutePath(path) | Get absolute file path |
Logging and Output
| Method | Description |
|---|---|
karate.log(...args) | Log message |
karate.logger.debug(...args) | Debug level log |
karate.embed(bytes, mimeType) | Embed in HTML report |
karate.render(template) | Render HTML template |
karate.doc(html) | Render and embed in report |
* karate.log('Processing:', response.id)
* karate.embed(screenshot, 'image/png')
Flow Control
| Method | Description |
|---|---|
karate.abort() | Exit scenario (not failed) |
karate.fail(message) | Fail with message |
karate.stop(port) | Pause for debugging |
karate.pause(ms) | Sleep (only in perf mode by default) |
* if (condition) karate.abort()
* if (!valid) karate.fail('Validation failed: ' + message)
Async Methods
| Method | Description |
|---|---|
karate.signal(result) | Trigger async event |
karate.listen(timeout) | Wait for signal (keyword) |
karate.waitForHttp(url) | Wait for HTTP endpoint |
karate.waitForPort(host, port) | Wait for TCP port |
karate.webSocket(url, [handler], [options]) | WebSocket connection |
karate.webSocketBinary(url, ...) | Binary WebSocket |
* def socket = karate.webSocket(wsUrl, handler)
* socket.send('hello')
* listen 5000
* match listenResult == 'response'
OS/Exec Methods
| Method | Description |
|---|---|
karate.exec(command) | Execute OS command (blocking) |
karate.fork(command) | Execute OS command (non-blocking) |
* def output = karate.exec('ls -la')
* def proc = karate.fork('npm start')
Setup Methods
| Method | Description |
|---|---|
karate.setup([name]) | Call @setup scenario |
karate.setupOnce([name]) | Call @setup once (cached) |
karate.configure(key, value) | Set configuration |
karate.start(mockFeature) | Start mock server |
karate.http(url) | HTTP request builder |
karate.target(object) | UI target lifecycle |
karate.embed(data | {name, parts, meta}) | Attach content to the report (single- or multi-part) |
Magic Variables
Special variables available in certain contexts.
Call Arguments
Available in called features:
| Variable | Description |
|---|---|
__arg | Single argument passed to call (null if none) |
__loop | Loop iteration index (-1 if not in loop) |
# In called feature:
* def data = __arg
* print 'Iteration:', __loop
Scenario Outline Variables
Available in Scenario Outline Examples:
| Variable | Description |
|---|---|
__row | Entire row as JSON object |
__num | Row index (0-based) |
Feature: Outline magic variables
Scenario Outline: Test with row data
* print 'Row index:', __num
* print 'Full row:', __row
* match __row.name == name
Examples:
| name | age |
| John | 30 |
| Jane | 25 |
Async Variables
| Variable | Description |
|---|---|
listenResult | Result from karate.signal() after listen |
* listen 5000
* match listenResult == { type: 'done' }
Mock Server Keywords
Used in mock feature files. See Test Doubles for details.
Request Matching
| Function | Description |
|---|---|
pathMatches(pattern) | Match URL path with placeholders |
paramExists(name) | Check query parameter exists |
paramValue(name) | Get query parameter value |
methodIs(method) | Match HTTP method |
typeContains(text) | Match Content-Type |
acceptContains(text) | Match Accept header |
headerContains(name, value) | Match header value |
bodyPath(path) | Extract from request body |
Scenario: pathMatches('/users/{id}') && methodIs('get')
* def response = { id: pathParams.id, name: 'John' }
Request Variables
| Variable | Description |
|---|---|
request | Request body |
requestBytes | Request as byte array |
requestPath | Request path |
requestUrlBase | Base URL |
requestUri | Full URI with query |
requestMethod | HTTP method |
requestHeaders | Request headers |
requestParams | Query parameters |
requestParts | Multipart parts |
pathParams | Path parameters from pathMatches |
Tags
Special tags that control test behavior.
@ignore
Skip scenario at runtime:
@ignore
Scenario: This test is disabled
@setup
Mark a scenario as a setup scenario. @setup is a reserved tag: the scenario is skipped during normal execution and is run only when invoked explicitly — either via karate.setup() / karate.setupOnce() (typically to feed a dynamic Scenario Outline) or by calling it by tag.
@setup
Scenario: Initialize test data
* def users = [{id: 1}, {id: 2}]
Because it is skipped from the normal sweep, an @setup scenario also makes a convenient reusable fixture that other scenarios pull in by tag — same file with read('@setup'), or across files with read('classpath:common.feature@setup'):
@setup
Scenario: Initialize test data
* def users = [{id: 1}, {id: 2}]
Scenario: Use the fixture
# no assignment = shared scope, so 'users' merges into this scenario
* call read('@setup')
* match users == '#[2]'
@lock
Serialize scenarios that share mutable state, or run a scenario exclusively. @lock is the v2 replacement for v1's @parallel=false (which is silently ignored):
@lock=database
Scenario: only one @lock=database scenario runs at a time
@lock=*
Scenario: runs alone — no other scenarios concurrent
@lock=<name>— named lock; scenarios sharing the name serialize, others run in parallel@lock=*— exclusive lock; the scenario runs alone
Place @lock=<name> on a Feature: line to apply it to every scenario in that feature (the drop-in for v1's feature-level @parallel=false). See @lock Tag.
@report=false
Hide from HTML / Cucumber JSON / JUnit XML / JSONL artifacts. The scenario still runs and counts toward suite totals; failures surface a redacted message and full detail goes only to runtime logs. Suppression propagates to features called from a tagged scenario.
@report=false
Scenario: warmup with sensitive credentials
* call read('classpath:auth/login.feature')
See Logging > Hide a scenario from reports.
@env
Run only when karate.env matches:
@env=dev
Scenario: Runs only in dev environment
@env=dev,staging
Scenario: Runs in dev or staging
@envnot
Run when karate.env does NOT match:
@envnot=prod
Scenario: Never runs in production
See Also
- Feature Files - Feature file structure
- Variables - Variable usage
- Match Keyword - Assertion patterns
- HTTP Requests - HTTP testing
- Configuration - Configuration options
- Test Doubles - Mock server details
- Karate Object - Detailed karate object reference