write-test · git:20260930.72181e1 · 2026-09-30 · sha256 8fd03f33c51a4859
write-test git:20260930.72181e1A
Immutable. This exact content never changes and is served at /api/v1/blob/8fd03f33c51a4859.
--- name: write-test description: > Generate tests for code that already exists. Use when a module has no tests, coverage is thin, a fix needs a regression test, or a change should land with proof it still works. Auto-triggers on: "write tests for", "add test coverage", "this needs tests" allowed-tools: Read, Write, Edit, Bash, Grep, Glob metadata: stage: beta since: "1.0.0" --- # Write Tests ## 0. Detect the framework - Run `devil facts` — it reports the detected test framework. Write in THAT framework, in its idiom (see `rules/test-frameworks.md`, which a host that installed the kit as a plugin has only after `devil setup`; the detection below is the fact the step needs and does not depend on it). - `rules/test-frameworks.md` names the idioms per stack. To detect, read the project's own manifest (`package.json`, `pyproject.toml`, `go.mod`, `Cargo.toml`, `build.gradle`, `pom.xml`) for a test dependency, then read the tests already in the tree for the framework they import. The manifest alone is not proof: a dependency can sit installed and unused, and the existing tests are what a new one has to sit next to. Both agreeing, or one of them, is the answer. - Write in THAT framework. Don't introduce a second framework; don't hand-roll asserts/mocks it already ships. - Nothing in the manifest and no tests in the tree? Pick the canonical default for the stack and say why in one line. ## 1. Read the source - Understand every public function's contract - Identify edge cases from the implementation (not just happy path) ## 2. Design test cases (before writing any code) For each function, list: - Happy path (normal input → expected output) - Boundary (empty, zero, max, nil/null) - Error path (invalid input, resource failure) - Concurrency (if the function touches shared state) Present the test plan. Wait for approval. ## 3. Write - Table-driven / parameterized tests in the detected framework's idiom (Go subtests, `rstest`, `it.each`, `pytest.mark.parametrize`) - Property-based tests for anything parsing external input (Hypothesis, proptest, fast-check, `testing/quick`) - One test function per behavior, not per source function - Test names describe the scenario: `test_login_rejects_expired_token` - No test depends on another test's state - No sleep() — use channels, signals, or mocks ## 4. Verify - All new tests pass - All existing tests still pass - No flaky tests (run 3 times) - Report coverage delta ## Report | Metric | Value | |---|---| | Framework detected | | | Tests added (passing) | | | Existing tests still green | | | Coverage delta | | | Behaviors still untested | | Plus: the test command you ran and its output, any case you deliberately did not cover and why, and anything the source does that you could not pin down from the outside.