Testing

Test Writer

Write tests for the behavior a diff actually introduced -- happy path, the edge cases that diff makes possible, and the failure modes a reviewer would ask about.

When to use this

  • New function/endpoint/component just landed without tests.
  • A bug fix needs a regression test that fails on the old code and passes on the new.
  • Not for retrofitting full coverage onto a legacy file unrelated to the current change -- scope to what changed.

The skill file

Copy this verbatim. It's written in the SKILL.md format (YAML frontmatter + markdown instructions) that Claude Code, and increasingly other agent tools, read directly.

SKILL.md
---
name: test-writer
description: Write tests for the specific behavior introduced or changed by a diff -- happy path, edge cases the change makes reachable, and a failing-then-passing regression test for bug fixes. Use after code changes, not to backfill unrelated legacy coverage.
---

# Test Writer

## For new functionality

1. Identify the observable behavior: inputs -> outputs, side effects,
   errors raised.
2. Write the happy-path test first.
3. Add edge cases specific to this code: boundary values (0, empty,
   max), the branches the diff's conditionals introduce, and any input
   validation the code performs.
4. Match the existing test file's framework, assertion style, and fixture/
   setup conventions -- don't introduce a new testing pattern for one file.

## For a bug fix

1. Write a test that reproduces the bug: it must **fail** against the
   pre-fix code and **pass** against the fix. If you can't make it fail
   first, you haven't actually captured the bug.
2. Keep the test minimal -- the smallest input that exercises the broken
   path, not a sprawling integration scenario.

## What not to do

- Don't test the language/framework itself (e.g. asserting a getter
  returns what you just set).
- Don't write a test so tightly coupled to implementation that any
  refactor breaks it without a behavior change -- assert on outputs/
  observable state, not internals, unless internals are the point.
- Don't skip error-path tests because the happy path is more interesting.

## Output

The test file(s), plus a one-line note on what each new test actually
proves, so the reviewer can check it lines up with the change.

Installing it elsewhere

The frontmatter/body split above is Claude Code's convention. Here's how to carry the same instructions into other tools:

Claude Code
.claude/skills/test-writer/SKILL.md

Save the file below verbatim (frontmatter included) at that path, project-local or in ~/.claude/skills/ for a user-level skill. Claude Code loads the name/description pair to decide when to pull it in, or you invoke it directly as /test-writer.

Cursor
.cursor/rules/test-writer.mdc

Convert the YAML frontmatter to Cursor's rule format (description, globs, alwaysApply: false) and keep the markdown body as the rule content. Cursor surfaces it by description match, same idea as Claude Code's auto-load.

Codex CLI / Copilot
AGENTS.md

Codex CLI (and increasingly other agentic CLIs) read AGENTS.md at the repo root as always-on instructions. Paste the markdown body under a heading like ## {title}; for GitHub Copilot's coding agent, the equivalent file is .github/copilot-instructions.md.

Windsurf
.windsurfrules

Append the markdown body to .windsurfrules at the repo root. Windsurf treats the whole file as always-on context, so keep only the instructions you want applied on every request.

Where this goes wrong
  • Writing a test that passes even without the fix -- always check it fails on the old code first for a regression test.
  • Asserting on internal implementation details that will break on any refactor, even a correct one.
  • Copy-pasting an existing test and forgetting to change the assertion, so it silently checks the wrong thing.