Testing Skill
Reference skill for generator teammates. Read this before writing any test code.
Test Strategy
Tests run in three layers. Each layer has a distinct purpose and cost profile.
Layer 1 — Unit Tests
- Test a single function or class in isolation.
- No network, no database, no file system.
- Mock only external boundaries (see code-gen SKILL.md for mock rules).
- Fast: all unit tests must complete in under 10 seconds total.
- Location:
tests/unit/mirroring the source tree.
Layer 2 — Integration Tests
- Test interactions between two or more modules (e.g., service + repository against a real test DB).
- Use a real database in a Docker container, not SQLite substitutes unless explicitly approved.
- Seed data is reset between tests using transactions or truncation.
- Location:
tests/integration/.
Layer 3 — End-to-End Tests (E2E)
- Test complete user flows through the running application.
- Use Playwright (see
references/playwright.mdfor config and patterns). - Run against a locally started application with a seeded test database.
- Location:
tests/e2e/.
Coverage Requirements
| Layer | Minimum Threshold |
|---|---|
| Unit | 100% of business logic branches |
| Integration | All happy paths + documented error paths per endpoint |
| E2E | All user stories in the current sprint contract |
Coverage tools (pytest-cov, Vitest coverage) must pass CI gates. A failing coverage gate blocks merge — it is not advisory.
Boundary Condition Generation
For every function under test, generate test cases for:
- Empty inputs — empty string, empty array, zero, null/None.
- Boundary values — min/max valid range, one below min, one above max.
- Invalid types — if the language allows runtime type errors, test them.
- Error paths — every documented exception/error case must have a test.
- Concurrency — if the function is called concurrently, test for race conditions.
Name boundary tests descriptively:
"returns empty list when no items match filter""raises OrderNotFoundError when order_id does not exist""caps quantity at MAX_ITEMS_PER_ORDER when input exceeds limit"
Test Data Rules
- Use realistic domain values in all tests. See
references/test-data.mdfor fixture patterns. - Never use placeholder values:
"test",0,"foo",nullas stand-ins for real domain objects. - Use factory functions or builder patterns to construct test data — not inline object literals.
- Randomize test data where possible using seeded fakers (Faker.js, Faker for Python).
- Seed the faker in CI to get deterministic results (
faker.seed(12345)).
- Seed the faker in CI to get deterministic results (
Gotchas
- Mocking business logic instead of testing it (hides bugs, creates false confidence)
- Writing tests that only test the happy path — error paths matter equally
- Using
time.sleep()orwaitForTimeoutin tests — use proper async patterns - Tests that depend on execution order — each test must be independently runnable
- Asserting on implementation details (private method calls) instead of observable outcomes
- Using production database credentials in any test environment
- Hardcoded port numbers without fallback — use dynamic port allocation in integration tests