Test layering policy
Unit tests
Purpose: verify individual functions and invariants in isolation.
- Data-driven: parameterized tables covering happy path, boundary, error, and edge cases.
- Property-based: fuzz invariants that must hold across all inputs (e.g., idempotency, sort stability, roundtrip serialization).
- Derive cases from the module's public API surface: input types/constraints, output shape, error modes, invariants.
Integration / contract tests
Purpose: verify interactions between components and external services.
- API envelope: request/response shape, status codes, content types, pagination.
- Error contract: error codes, error shapes, rate limiting, retries.
- Auth and scoping: token validation, role-based access, tenant isolation.
- Eventual consistency: verify convergence within bounded time; poll rather than sleep.
- Reuse auth state across tests where possible; avoid redundant login flows.
E2E tests
Purpose: verify real user workflows through the full stack.
- No mocks; exercise real services, databases, and APIs.
- Happy-path workflows only; save edge cases for lower layers.
- State-tolerant: never assume a clean slate; tolerate and work with prior state.
- Idempotent: safe to run repeatedly without cleanup between runs.
- Flow-oriented: validate real data paths end-to-end rather than isolated assertions.
Hard rules
- Never invent signatures, source locations, or line numbers. Only reference what you have read from the codebase.
- No fabricated fixtures. Derive test data from actual schemas, types, or seed data in the repo.
- No test-only hacks in product code. No
if (process.env.TEST) branches, no test-specific exports, no test backdoors.
- E2E must not rely on clean slate. Tests must tolerate pre-existing data, prior test runs, and shared environments.
Execution guidance
Preflight checks (before e2e)
- Verify the target environment is reachable (health endpoint, ping).
- Confirm required services are running (database, API, auth provider).
- Validate test user / credentials exist and are functional.
- Check for leftover state that could cause false failures; log it, do not fail on it.
Deterministic fixtures
- Use seeded randomness for generated data (seeded faker, deterministic UUIDs).
- Fixtures should be self-contained; avoid cross-test fixture dependencies.
- Prefer factory functions over shared mutable fixture objects.
Async handling
- Poll with bounded timeout and backoff; never use fixed
sleep/waitForTimeout.
- Set explicit timeout per operation; fail fast with a descriptive message on timeout.
- Bound retry attempts (e.g., max 3 retries with exponential backoff).
- Use framework-native waiting (Playwright
expect, async assertions) over manual loops.
Flake handling
- Single infrastructure retry per test run; if it fails twice, it is not flake.
- On retry failure, collect diagnostics: screenshots, network logs, service health, timestamps.
- Classify the failure (flaky / outdated / bug) before attempting a fix.
- Never add arbitrary delays or retry loops as a flake "fix."
API surface discovery
Before generating test cases:
- Read the module source to enumerate exports/public functions.
- Confirm scope from the user request and inspected code context; if ambiguous, state assumptions and proceed conservatively.
- For each function: input types/constraints, output shape, error modes, invariants.
- Probe for state dependencies and ordering constraints between functions.
Output format
Use markdown. Produce three sections:
Test Strategy -- one bullet per layer (unit/integration/e2e) naming the functions/flows and their coverage type.
Test Matrix -- table per function: columns ID | Category | Name | Input | Expected. Case ID scheme: {CATEGORY}-{NN} (HP, BV, ERR, EDGE). Append-only; never renumber.
Implementation Plan -- ordered steps: fixtures, unit tests, integration tests, e2e flows, run command.
CI guidance
Fast PR smoke lane
- Unit tests + linting + type-check on every PR.
- Subset of integration tests covering critical contracts.
- Target: under 5 minutes.
Nightly full lane
Full unit + integration + e2e suite with higher property-based iteration counts. Flag tests that pass on retry but failed initially.
Workflow
- Spec or code defines the module behavior (types, constraints, API surface).
- Agent (with this skill) produces test strategy, matrix, and implementation plan.
- test-writer agent translates the plan to runnable code in the target language's idiom.
- Developer implements to pass the tests.
- If implementation reveals missing cases, propose them first; append to spec only when explicitly requested.
1---2name: testing-best-practices3description: Use when designing tests, writing test cases, or planning test strategy for a module. Covers unit, integration, and e2e layering.4---5
6## Test layering policy
7
8### Unit tests
9
10Purpose: verify individual functions and invariants in isolation.
11
12- **Data-driven**: parameterized tables covering happy path, boundary, error, and edge cases.
13- **Property-based**: fuzz invariants that must hold across all inputs (e.g., idempotency, sort stability, roundtrip serialization).
14- Derive cases from the module's public API surface: input types/constraints, output shape, error modes, invariants.
15
16### Integration / contract tests
17
18Purpose: verify interactions between components and external services.
19
20- **API envelope**: request/response shape, status codes, content types, pagination.
21- **Error contract**: error codes, error shapes, rate limiting, retries.
22- **Auth and scoping**: token validation, role-based access, tenant isolation.
23- **Eventual consistency**: verify convergence within bounded time; poll rather than sleep.
24- Reuse auth state across tests where possible; avoid redundant login flows.
25
26### E2E tests
27
28Purpose: verify real user workflows through the full stack.
29
30- No mocks; exercise real services, databases, and APIs.
31- Happy-path workflows only; save edge cases for lower layers.
32- **State-tolerant**: never assume a clean slate; tolerate and work with prior state.
33- **Idempotent**: safe to run repeatedly without cleanup between runs.
34- **Flow-oriented**: validate real data paths end-to-end rather than isolated assertions.
35
36## Hard rules
37
38- **Never invent signatures, source locations, or line numbers.** Only reference what you have read from the codebase.
39- **No fabricated fixtures.** Derive test data from actual schemas, types, or seed data in the repo.
40- **No test-only hacks in product code.** No `if (process.env.TEST)` branches, no test-specific exports, no test backdoors.
41- **E2E must not rely on clean slate.** Tests must tolerate pre-existing data, prior test runs, and shared environments.
42
43## Execution guidance
44
45### Preflight checks (before e2e)
46
471. Verify the target environment is reachable (health endpoint, ping).
482. Confirm required services are running (database, API, auth provider).
493. Validate test user / credentials exist and are functional.
504. Check for leftover state that could cause false failures; log it, do not fail on it.
51
52### Deterministic fixtures
53
54- Use seeded randomness for generated data (seeded faker, deterministic UUIDs).
55- Fixtures should be self-contained; avoid cross-test fixture dependencies.
56- Prefer factory functions over shared mutable fixture objects.
57
58### Async handling
59
60- Poll with bounded timeout and backoff; never use fixed `sleep`/`waitForTimeout`.
61- Set explicit timeout per operation; fail fast with a descriptive message on timeout.
62- Bound retry attempts (e.g., max 3 retries with exponential backoff).
63- Use framework-native waiting (Playwright `expect`, async assertions) over manual loops.
64
65### Flake handling
66
67- **Single infrastructure retry** per test run; if it fails twice, it is not flake.
68- On retry failure, collect diagnostics: screenshots, network logs, service health, timestamps.
69- Classify the failure (flaky / outdated / bug) before attempting a fix.
70- Never add arbitrary delays or retry loops as a flake "fix."
71
72## API surface discovery
73
74Before generating test cases:
75- Read the module source to enumerate exports/public functions.
76- Confirm scope from the user request and inspected code context; if ambiguous, state assumptions and proceed conservatively.
77- For each function: input types/constraints, output shape, error modes, invariants.
78- Probe for state dependencies and ordering constraints between functions.
79
80## Output format
81
82Use markdown. Produce three sections:
83
84**Test Strategy** -- one bullet per layer (unit/integration/e2e) naming the functions/flows and their coverage type.
85
86**Test Matrix** -- table per function: columns `ID | Category | Name | Input | Expected`. Case ID scheme: `{CATEGORY}-{NN}` (HP, BV, ERR, EDGE). Append-only; never renumber.
87
88**Implementation Plan** -- ordered steps: fixtures, unit tests, integration tests, e2e flows, run command.
89
90## CI guidance
91
92### Fast PR smoke lane
93
94- Unit tests + linting + type-check on every PR.
95- Subset of integration tests covering critical contracts.
96- Target: under 5 minutes.
97
98### Nightly full lane
99
100Full unit + integration + e2e suite with higher property-based iteration counts. Flag tests that pass on retry but failed initially.
101
102## Workflow
103
1041. Spec or code defines the module behavior (types, constraints, API surface).
1052. Agent (with this skill) produces test strategy, matrix, and implementation plan.
1063. test-writer agent translates the plan to runnable code in the target language's idiom.
1074. Developer implements to pass the tests.
1085. If implementation reveals missing cases, propose them first; append to spec only when explicitly requested.