Variables
Define test data upfront and extract runtime values to chain multi-step workflows.
Defining variables
Variables are key-value pairs defined at the definition level or test level. Values can be any JSON type — strings, numbers, booleans, arrays, or objects:
{
"name": "my-suite",
"variables": {
"baseEmail": "test@example.com",
"retryCount": 3,
"enableAuth": true,
"allowedRoles": ["admin", "editor"],
"defaultUser": { "name": "Alice", "email": "alice@example.com" }
},
"tests": [
{
"name": "Test 1",
"variables": {
"testEmail": "specific@example.com"
},
"steps": [ ... ]
}
]
}Interpolation: {{variableName}}
Reference variables with {{variableName}} in item fields, actions, and assertions:
| Where | Resolved | Example |
|---|---|---|
| Item env values | Build time | "value": "redis://:{{REDIS_PASSWORD}}@my-redis:6379" |
| Item images | Build time | "image": "{{REGISTRY}}/my-service:latest" |
| Action URLs | Runtime | "url": "api-gateway/api/users/{{userId}}" |
| Action headers | Runtime | "Authorization": "Bearer {{token}}" |
| Action body (string values) | Runtime | "email": "{{email}}" |
| Database queries | Runtime | "query": "SELECT * FROM users WHERE id = {{userId}}" |
| Assertion values | Runtime | "value": "{{email}}" |
| Match where values | Runtime | "value": "user-service/{{path}}" |
| Console log messages | Runtime | "value": "User {{userId}} created" |
Type preservation
When {{var}} is the entire value of a field, the typed value is preserved. When embedded in a larger string, the value is stringified:
"value": "{{retryCount}}"— preserves the number3"url": "api/users/{{userId}}"— stringifies into the URL"body": { "filters": "{{filterObj}}" }— preserves the object"body": { "label": "Count: {{count}}" }— stringifies to"Count: 3"
Dotted path access
Variable references support dotted paths and array indexing: {{user.email}}, {{users[0].name}}, {{config.retries}}.
Extraction
Extract values from responses at runtime using JSONPath expressions:
{
"name": "Create user",
"action": {
"type": "httpRequest",
"method": "POST",
"url": "api-gateway/api/users",
"body": { "email": "{{testEmail}}" }
},
"extract": {
"userId": "$.response.body.id",
"authToken": "$.response.headers.x-auth-token",
"firstName": "$.response.body.user.profile.name",
"firstItem": "$.response.body.items[0].id"
}
}Extracted values become variables available in all subsequent steps.
Regex extraction
Extract rules can also be regex extract objects — resolve a JSONPath first, then apply a regex pattern to pull out a substring:
{
"extract": {
"userId": "$.response.body.id",
"correlationId": {
"path": "$.response.body.message",
"pattern": "correlation_id=([a-f0-9-]+)",
"group": 1
}
}
}| Field | Type | Required | Description |
|---|---|---|---|
path | string | Yes | JSONPath to the source value |
pattern | string | Yes | Regex pattern (with capture groups) |
group | number | No | Capture group index (default: 1; use 0 for full match) |
The JSONPath is resolved first, the result is coerced to a string, then the regex is applied. An error is raised if the path doesn't exist, the pattern doesn't match, or the capture group is out of range.
When to use regex extraction. Use the simple string form when the value you need is already a discrete field. Use the regex form when the value is embedded inside a larger string — for example, extracting an ID from a log message or a token from a URL.
Extract paths
For HTTP responses:
$.response.body.field— response body field$.response.status— HTTP status code$.response.headers.header-name— response header (case-insensitive)$.response.body.nested.field— nested access$.response.body.array[0]— array index
For database query results:
$.response.data[0].field— column from a result row$.response.success— whether the query succeeded
Unified root context. Extract and assertion paths use the same unified root context. See Assertions for the full path reference.
Extraction in assertion blocks
You can also extract values from within assertion blocks — useful for capturing values from intercepted inter-service traffic:
{
"match": {
"path": "$.traffic",
"where": [
{ "path": "$$.origin", "operator": "eq", "value": "api-gateway" },
{ "path": "$$.request.method", "operator": "eq", "value": "POST" },
{ "path": "$$.request.url", "operator": "contains", "value": "order-service/api/orders" }
],
"count": 1
},
"extract": {
"internalOrderId": "$.match.response.body.orderId"
},
"assertions": [
{ "path": "$.match.response.status", "operator": "eq", "value": 201 }
]
}Precedence
In test steps (runtime), variables come from three sources in order of increasing precedence:
- Definition-level
variables— shared across all tests - Test-level
variables— per-test overrides extracton steps — runtime extraction (overwrites all hardcoded values)
In item fields (build time), the resolver assembles one flat map seeded with config.yaml env entries, then definition-level variables are merged on top. All {{VAR}} in items resolve against this merged map. For keys not overridden by a definition variable, {{FOO}} and ${{FOO}} resolve to the same value. Unresolved references are errors.
Scoping rules
- Definition-level variables are seeded first, then test-level variables override
- Variables persist across all groups within a single test run
- Extracted variables from group N are available in groups N+1, N+2, etc.
- Within a parallel group, variable extraction order is non-deterministic
- Referencing an undefined variable causes an immediate error
Build-time vs runtime variables
| Syntax | Resolved | Scope | Purpose |
|---|---|---|---|
${{VAR}} | Build time | Entire definition | Config values from config.yaml env only |
{{VAR}} | Build time | Item fields | Merged map of config.yaml env + definition-level variables |
{{VAR}} | Runtime | Test steps | Variables from variables / extract / loops |
This means definition-level variables can share values across items — passwords, hostnames, image tags — without duplication:
{
"name": "my-tests",
"variables": {
"REDIS_PASSWORD": "changeme"
},
"items": [
{
"type": "DATABASE",
"name": "my-redis",
"database": "redis",
"dbPassword": "{{REDIS_PASSWORD}}"
},
{
"type": "SERVICE",
"name": "my-server",
"port": 3000,
"healthCheck": "/health",
"env": [
{ "name": "REDIS_URL", "value": "redis://:{{REDIS_PASSWORD}}@my-redis:6379" }
]
}
]
}You can also combine ${{}} and {{}} — use ${{}} inside a variables value to compose variables from config:
{
"variables": {
"baseUrl": "${{API_HOST}}/v1"
}
}