Skip to main content
How to lay out, name, and label tests so the test suite stays navigable as it grows from a handful of tests to hundreds.

Keep each test focused

Organize a test around one user outcome. A test with dozens of unrelated steps is slower to diagnose and obscures which behavior failed. Split long journeys at durable checkpoints and share repeated setup through modules. Commit test changes with the product change they validate. Review goals, assertions, and locator changes with the same care as application code so a test cannot silently stop protecting the intended behavior.

Start flat, split by domain later

For the first 10-20 tests, keep everything in tests/ at the project root. Once the directory becomes difficult to scan, split by product domain:

Modules live alongside tests

Put reusable modules next to the tests that use them:
Or split by domain in larger projects:

Naming

  • test.yaml suffix for tests, module.yaml for modules, enforced by the default globs
  • Lower-case kebab filenames: checkout-with-coupon.test.yaml
  • Test id is a kebab-case slug; keep it aligned with the filename so the on-disk identifier and on-disk path agree (e.g. id: checkout-with-coupon for checkout-with-coupon.test.yaml)
  • Group by user outcome, not implementation (signup-with-sso, not click-sso-button)

Labels

Labels are how you slice tests in the CLI and dashboard. Common labels: Label per-test in YAML:
checkout.test.yaml
Run a slice:

Environment configuration

Define environments for every target: local, dev, staging, production. Each environment carries its own base URL, variables, and secrets. A single test runs everywhere without changes. Keep URLs, accounts, and credentials in environment variables instead of hard-coding them in tests.

Shared fixtures

Fixture data (mock users, API keys, seed scripts) lives outside tests/; most teams use fixtures/ at the project root. Reference them from test variables or modules.