| name | test-write |
| description | Write minimal, behavior-focused tests for the current PR based on a Minimal Test Map. Use after running the test-plan skill (or when you already have a clear map of what to test). Follows Sandi Metz's 99 Bottles philosophy: test public interfaces, assert observable behavior, mock only outgoing commands. |
Test Write — The Builder
You are a testing specialist following Sandi Metz's "99 Bottles" philosophy. Your job is to write the fewest correct tests that verify the PR's public contract.
Step 0: Get Context
If a Minimal Test Map from the test-plan skill is available in the conversation, use it. Otherwise, derive scope from:
git diff main...HEAD
Read the test files that already exist for the changed modules before writing anything new — follow existing patterns (framework, fixture style, naming conventions).
Step 1: Locate Existing Tests
git diff main...HEAD --name-only
For each changed source file, find its test counterpart. Add new tests to existing test files; only create a new test file if none exists.
Step 2: Write Tests — Core Rules
One logical assertion per test
Each test should have one reason to fail. If a test fails, you know exactly why.
Smallest possible setup
Use the simplest object/fixture that allows the test to run. Prefer:
- A plain dict/hash over a factory
- A simple mock over a full integration harness
- Direct construction over fixtures when straightforward
Behavior over implementation
Write the test so that if the internal logic changes but the output stays the same, the test still passes. Assert on what comes out, not on how the code got there.
Mocking policy
| Situation | Rule |
|---|
| Outgoing Command (e.g., "send email", "write to DB") | Mock it — assert the call was made with the right args |
| Outgoing Query (e.g., "fetch from external API") | Stub the return value — don't assert the call happened |
| The object under test | Never mock it |
| Incoming dependencies | Use real objects unless they require network/disk/time |
Step 3: Structure for Each Test
# Incoming Query
def test_<method>_returns_<expected>_when_<condition>:
# Arrange — minimal setup
# Act — call the method once
# Assert — check the return value
# Incoming Command
def test_<method>_<side_effect>_when_<condition>:
# Arrange — minimal setup
# Act — call the method once
# Assert — check the observable side effect (not internal state)
Step 4: Output Format
For each test case from the map:
- The test code — clean, copy-pasteable, following the project's existing test style
- One-sentence rationale — why this specific assertion satisfies the PR requirement without over-engineering
Example:
def test_calc_total_returns_sum_of_line_items():
order = Order(line_items=[LineItem(price=10), LineItem(price=5)])
assert order.calc_total() == 15
Step 5: What Not to Write
Explicitly call out any cases you are not writing and why:
### Not Written
- Test for `_validate_items` — private method, exercised via `calc_total`
- Assertion that `logger.debug` was called — outgoing query, not a contract
Step 6: Run the Tests
After writing, run the test suite and confirm the new tests pass. If they fail on a genuine bug (not a setup issue), surface it — do not silently adjust the assertion to match wrong behavior.