Documentation Index
Fetch the complete documentation index at: https://docs.ellomas.com/llms.txt
Use this file to discover all available pages before exploring further.
Best Practices
Organise Workflows by Domain
Group related workflows in a directory structure:
tests/
├── auth/
│ ├── login.yaml
│ ├── registration.yaml
│ └── password-reset.yaml
├── api/
│ ├── crud-users.yaml
│ └── pagination.yaml
└── db/
├── seed.yaml
└── migrations.yaml
Run all tests in parallel:
replay run tests/**/*.yaml --concurrency 4
Use Config Profiles for Environments
Never hard-code environment URLs or credentials:
# replay.yaml
profiles:
dev:
config:
http:
base_url: http://localhost:3000
postgres:
dsn: postgres://dev:pass@localhost:5433/devdb
staging:
config:
http:
base_url: https://staging.example.com
postgres:
dsn: postgres://deploy:pass@staging-db:5432/stagingdb
replay run tests/ --profile staging
Extract values as soon as they become available and assert at every meaningful checkpoint:
- name: login
type: http
request:
method: POST
url: /auth/login
extract:
token: $.data.token
assert:
- ["$.status", "eq", 200]
- ["$.data.token", "not_null"]
- name: get-profile
type: http
request:
method: GET
url: /profile
headers:
Authorization: Bearer {{ token }}
assert:
- ["$.data.email", "not_null"]
- ["$.data.verified", "eq", true]
This makes failures easy to pinpoint — you know exactly which step broke.
Keep Steps Focused
Each step should do one thing well. Avoid steps with multiple responsibilities:
# Good
- name: create-user
type: http
request:
method: POST
url: /users
body:
email: test@example.com
- name: verify-user-in-db
type: db
query: "SELECT id FROM users WHERE email = 'test@example.com'"
assert:
- ["$[0].id", "not_null"]
# Bad — mixing concerns in one step
- name: create-and-verify
type: http
request:
method: POST
url: /users
body:
email: test@example.com
Use Deterministic Data
Avoid random values that make tests flaky. When you need unique data, use {{ nowUnix }} or a counter:
- name: create-user
type: http
request:
method: POST
url: /users
body:
email: "qa-{{ nowUnix }}@example.com"
Use ignore_error Sparingly
ignore_error is useful for cleanup steps or optional checks, but overusing it hides real failures:
# Good — cleanup that should not fail the pipeline
- name: cleanup
type: shell
command: "rm -rf /tmp/test-data"
ignore_error: true
# Avoid — this hides a real test failure
- name: critical-check
type: http
request:
method: GET
url: /health
assert:
- ["$.status", "eq", 200]
ignore_error: true # ← hides failures!
Template Sensitive Data
Use variables for secrets and credentials:
- name: login
type: http
request:
method: POST
url: /auth/login
body:
email: "{{ API_USERNAME }}"
password: "{{ API_PASSWORD }}"
Pass them via environment variables or the config file — never hard-code them in workflow files.
Clean Up Test Data
When your workflow creates data, clean it up afterward:
- name: create-test-data
type: http
request:
method: POST
url: /users
extract:
test_user_id: $.data.id
# ... test logic ...
- name: delete-test-data
type: http
request:
method: DELETE
url: /users/{{ test_user_id }}
ignore_error: true
Use call for Reusable Steps
Extract common patterns (auth, setup, teardown) into reusable files:
# auth.yaml
name: login
steps:
- name: login
type: http
request:
method: POST
url: /auth/login
body:
email: "{{ email }}"
password: "{{ password }}"
extract:
token: $.data.token
returns: [token]
# test.yaml
steps:
- name: authenticate
type: call
file: auth.yaml
with:
email: admin@example.com
password: "{{ ADMIN_PASSWORD }}"
returns: [token]
Version Your Workflows with the Schema
Add the JSON schema reference to get IDE autocomplete:
# yaml-language-server: $schema=https://raw.githubusercontent.com/tomiwa-a/replay/main/schema.json
Run Workflows in CI with --fail-fast
In CI, --fail-fast saves time by stopping at the first failure:
replay run tests/ --concurrency 4 --fail-fast
What’s Next?