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:
{
"name": "my-suite",
"variables": {
"baseEmail": "test@example.com"
},
"tests": [
{
"name": "Test 1",
"variables": {
"testEmail": "specific@example.com"
},
"steps": [ ... ]
}
]
} Interpolation: {{variableName}}
Reference variables with {{variableName}} anywhere in actions and assertions:
| Where | Example |
|---|---|
| Action URLs | "url": "api-gateway/api/users/{{userId}}" |
| Action headers | "Authorization": "Bearer {{token}}" |
| Action body (string values) | "email": "{{email}}" |
| Database queries | "query": "SELECT * FROM users WHERE id = {{userId}}" |
| Assertion values | "value": "{{email}}" |
| Match block fields | "url": "user-service/{{path}}" |
| Console log messages | "value": "User {{userId}} created" |
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": "$.body.id",
"authToken": "$.headers.x-auth-token",
"firstName": "$.body.user.profile.name",
"firstItem": "$.body.items[0].id"
}
} Extracted values become variables available in all subsequent steps.
Extract paths
For HTTP responses:
$.body.field— response body field$.statusCode— HTTP status code$.headers.header-name— response header (case-insensitive)$.body.nested.field— nested access$.body.array[0]— array index
For database query results:
$.data[0].field— column from a result row$.success— whether the query succeeded
Extract paths vs assertion paths. Extract paths use a flat document ($.body.field). Assertion paths use a nested format (response.body.field). Don't mix them — see Assertions for assertion path syntax.
Extraction in assertion blocks
You can also extract values from within assertion blocks — useful for capturing values from intercepted inter-service traffic:
{
"match": {
"origin": "api-gateway",
"method": "POST",
"url": "order-service/api/orders"
},
"extract": {
"internalOrderId": "$.response.body.orderId"
},
"assertions": [
{ "path": "response.status", "operator": "eq", "value": 201 }
]
} Precedence
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)
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 | By | Purpose |
|---|---|---|---|
${{VAR}} | Build time | Definition resolver | Config values from config.yaml env |
{{VAR}} | Runtime | Test agent | Variables from variables / extract |
You can combine both — use ${{}} inside a variables value to compose runtime variables from config:
{
"variables": {
"baseUrl": "${{API_HOST}}/v1"
}
}