write-tests
Write missing tests for existing code, or raise a package's coverage toward 100%, with the right kind of test for each path. Use when the user asks for tests or more coverage, when `bun run test:cover
SKILL.md.claude/skills/write-tests/SKILL.md
GitHubWrite tests, raise coverage
The test-writer agent does this end to end; this is its procedure, for doing it by
hand or checking its work. .claude/rules/tests.md has which kind of test goes where.
- Measure.
bun run --filter @repo/<name> coveragefor one package (unit and integration together, so the full Docker profile must fit), orbun run test:coveragefor all of them;bun run --cwd apps/ai coveragefor Python,bun run --filter @repo/mobile coveragefor mobile. They take minutes: run them in the background. Note every uncovered line and branch. - Pick the kind for each gap:
- pure logic: a unit test next to the file (
src/<file>.test.ts); - anything touching Postgres, Valkey, email, auth, queues or HTTP: an integration test
in the package's
test/(apps name them*.integration.test.ts), against the real service, as the service's own role, data fromfactories(db)in@repo/db/testing. In apps/api,test/harness.ts(startApi(<redis db>),createSession); - a user flow: a Playwright spec in
apps/web/e2e/orapps/mobile/e2e/; - a component: a story with a
playfunction inpackages/ui.
- pure logic: a unit test next to the file (
- Write them. Error branches too: the typed
AppErrorcodes, the early returns, another organization seeing nothing. Third parties through the local fakes, nevervi.mockof Prisma, Redis, BullMQ or a provider. No fixed sleeps: wait for the condition. No.skip,.onlyor coverage pragma (the edit hook refuses them). - Can't be tested automatically? Write the test anyway, skipped with the reason
(
it.skipIf(!process.env.<CREDENTIAL>)), and add a row to "Not tested automatically" indocs/testing.mdsaying why and how to run it by hand. - Reach 100%.
bun run test:coveragemerges every suite and names each file below 100%, with the lines, branches and functions no test reaches. A shared package counts what its callers' tests reach too. A line that truly can't be tested is either deleted or listed, with the reason, in "Coverage exceptions" indocs/testing.md.
Done when
- The new tests pass on their own and in the package's suite.
- Every file you touched is at 100% in
bun run test:coverage. bun run lint,bun run check-typespass; the verify skill for anything that ran integration tests.