Writing Tests

AI

Write tests that match the repo's existing conventions. Use when asked to add, fix, or improve test coverage for a module, function, or bug.

testingunitintegrationcoverage

Save this file as .agents/skills/writing-tests/SKILL.md in your repository.

Compatible with: Claude Code, GitHub Copilot, Cursor, Windsurf — any agent that reads SKILL.md-style instruction files.

---
name: writing-tests
description: Write tests that match the repo's existing conventions. Use when asked to add, fix, or improve test coverage for a module, function, or bug.
---

# Writing Tests

Tests must look like they were written by the team, not generated.

## Before writing anything

1. Find the existing tests: `rg --files -g '*test*' -g '*spec*'`
2. Read 2–3 of them. Note the framework, file naming, describe/it style, fixture factories, and assertion helpers in use.
3. Find how tests are run (package.json scripts, Makefile, CI config). Use exactly that command.
4. Never introduce a new framework, assertion library, or directory layout without asking.

## What to test

- **Behavior, not implementation.** Assert on inputs and outputs, not internal calls, unless the internal call IS the contract (e.g. "does not hit the database").
- **The branches that matter:** happy path, each error path, boundary values (empty, 0, max, off-by-one), invalid input.
- **Regression first:** if this work comes from a bug report, write the failing test that reproduces the bug before anything else.

## Writing rules

- One behavior per test; the test name states the expected behavior ("returns null when user is missing", not "test getUser").
- Arrange / Act / Assert, visibly separated.
- Deterministic: no real time, randomness, network, or wall-clock sleeps. Mock the boundary, inject the clock.
- Independent: tests pass in any order and in isolation (`--runInBand` or shuffle mode too).
- Mock sparingly. Prefer fakes/in-memory implementations over deep mock chains; if you mock everything you test nothing.
- Cover error messages and status codes, not just "it throws".

## Finishing

1. Run the new tests — watch them pass.
2. Mutate the source (break it on purpose) — watch the tests fail. Revert.
3. Run the full suite to check for interference.
4. Report: files added, cases covered, and any behavior you could not test and why.

Related skills: systematic-debugging, safe-refactoring

Related commands: npm test, vitest run, pytest -q

Related workflows: Ask an agent to create tests