References ($ref)
Share and reuse items, variables, and UI action steps across test files.
Basic usage
Items can reference shared fragments using $ref with a relative path:
{
"items": [
{ "$ref": "../shared/api-gateway.json" },
{ "$ref": "../shared/postgres-db.json" }
]
} The path is relative to the file containing the $ref. At resolution time, Dokkimi replaces the $ref with the full contents of the referenced file.
Overriding fields
Additional fields are shallow-merged as overrides on top of the fragment:
{
"items": [
{
"$ref": "../shared/postgres.json",
"initFilePath": "../init-files/users-seed.sql"
}
]
} The fragment's fields are used as the base, and inline fields override them.
Don't override name on a $ref unless you intentionally want a second instance with a different name. The fragment already has a name — overriding it creates a differently-named copy.
Multi-ref
$ref can also be an array of paths. Fragments are resolved and merged left-to-right, with later files overriding earlier ones. Inline fields still win over all fragments:
{
"items": [
{
"$ref": ["../shared/base-service.json", "../shared/staging-overrides.json"],
"env": ["...$ref.env", { "name": "EXTRA", "value": "inline-wins" }]
}
]
} Array spreading
To extend an array field from the fragment instead of replacing it, use "...$ref.<path>" as an element in the override array:
{
"$ref": "../shared/my-service.json",
"env": ["...$ref.env", { "name": "EXTRA_VAR", "value": "123" }]
} The "...$ref.env" marker is replaced with the fragment's env array. Position controls order — entries before the marker are prepended, entries after are appended. If the path doesn't resolve to an array, the marker expands to nothing.
The path supports dot notation for nested fields (e.g., "...$ref.some.nested.array").
$ref for variables
Both definition-level and test-level variables support $ref to load values from shared files:
{
"variables": {
"$ref": "../shared/db-vars.json",
"localOverride": "my-value"
}
} Multi-ref also works for variables:
{
"variables": {
"$ref": ["../shared/db-vars.json", "../shared/test-users.json"],
"extraVar": "inline-wins"
}
} Referenced files must be plain { "key": "value" } objects (no nested objects). Variable files can themselves use $ref to load from another variables file — recursive resolution with circular reference detection.
$ref for actions
Step actions support $ref to load reusable action definitions from shared fragments. This works for all action types — httpRequest, dbQuery, ui, and wait.
The fragment file must have an action field, and can include optional name and description metadata:
# shared/create-user.yaml
name: Create user
description: POST to create a new user
action:
type: httpRequest
method: POST
url: api-gateway/api/users
headers:
Content-Type: application/json
body:
name: "{{userName}}" Reference it on a step's action with $ref. Inline fields are shallow-merged as overrides:
- name: Create a user
action:
$ref: ../shared/create-user.yaml
body:
name: custom-name
assertions:
- assertions:
- path: response.statusCode
operator: eq
value: 201 UI sub-step $ref
UI actions also support $ref inside the steps array to splice in reusable sub-step sequences (e.g., a login flow). The fragment has a steps array instead of action:
# shared/login-flow.yaml
name: OAuth login flow
description: Signs in via Google OAuth mock
steps:
- visit: /login
- click: "[data-testid='login-btn']"
- waitFor: "[data-testid='dashboard']" Reference it inside a UI action's steps array:
action:
type: ui
target: my-app
steps:
- $ref: ../shared/login-flow.yaml
- visit: /settings
- click: "[data-testid='save']" The $ref entry is replaced by the fragment's steps array, spliced into position. Multiple $ref entries can appear in the same steps array.
Recursive refs
Fragments can themselves use $ref to build on other fragments. This lets you create layered definitions — a base service with common settings, then variants that override a few fields:
# shared/base-service.yaml
type: SERVICE
port: 3000
healthCheck: /health
env:
- name: NODE_ENV
value: production # shared/api-gateway.yaml
$ref: ./base-service.yaml
name: api-gateway
image: my-registry/api-gateway:latest
env:
- ...$ref.env
- name: DATABASE_URL
value: postgresql://dokkimi:dokkimi@postgres-db:5432/dokkimi At resolution time, Dokkimi walks the chain: base-service.yaml is loaded first, then api-gateway.yaml overrides on top, then any inline overrides in the test file win last. Each level is a shallow merge, same as single-level $ref.
This also works for action refs, UI sub-step refs, and variable refs. Circular references are detected and reported as validation errors. Chains are limited to 10 levels deep.
Rules
- Paths are relative to the file containing the
$ref - For multi-ref, fragments merge left-to-right; inline override fields win
- Refs are recursive — fragments can
$refother fragments, with circular reference detection - Maximum ref chain depth is 10 levels
$refis stripped before sending to the backend- Merging is shallow — no deep merging for objects