agentleFS
jjmrochajjmrocha/coding-skills/writing-unit-tests/SKILL.md

writing-unit-tests

Skill1 starsChanged 30 days agoMITView on GitHub

Use when writing unit tests for specific code — adding tests, improving coverage, or starting a TDD cycle. Triggers on "add tests", "write tests for X", or when modifying code that needs tests.

4 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

2 rules. 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 4 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use // given / // when / // then comments only if the existing codebase uses them.

    line 46
  2. Distinctive:

    If no reference exists for the project's language, apply the principles above and mirror existing project conventions.

    line 56

“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Writing Unit Tests

A unit test asserts observable behavior, never implementation. Given state + input, verify output + side effects the caller can observe. If you're asserting call counts, internal fields, or private methods, step back.

For a detailed human-readable guide, see docs/how-to-write-unit-tests.md.

Steps

  1. Inspect existing tests — if the project instructions (CLAUDE.md, CLAUDE.local.md) name reference test files, those are the convention: read them first and follow them even when tests next to the code under test differ — neighbours may be legacy; mention the drift once. Without named references, read 2-3 nearby test files. Note naming, assertion library, mock framework, fixture patterns. Project conventions override the language reference.
  2. List scenarios — enumerate cases across the four quadrants. Present for confirmation before writing (skip if user says proceed).
  3. Read the language reference — pick the matching file from the dispatch table below.
  4. Write tests — one behavior per test. AAA structure. Run the full suite — never leave it red.
  5. Prove the assertions bite — for a test whose contract is a side effect on a mock (a rollback, a commit, a publish), break that side effect once in the code under test, confirm the test fails, restore it. A test that passes both ways asserts nothing.

Scenario Quadrants

QuadrantWhat to verify
Happy pathValid inputs → expected successful output
Negative inputsNulls, empty, out-of-range, malformed — expect explicit errors or sentinel returns
Boundary conditions0, 1, off-by-one, max/min, overflow — bugs cluster here
Error pathsDependency failures: DB down, network timeout, third-party error response

FIRST-U

PrincipleRule
FastSingle-digit ms. No real I/O.
IsolatedNo shared state. Tests run in any order.
RepeatableNo real clock, RNG, network, or filesystem. Pin time, seed randomness.
Self-validatingExplicit assertions. No manual inspection.
TimelyWrite with the code, not months later.
UnderstandableName reads like a spec; body is short. A reader understands what's tested without reading the implementation.

Structure: Arrange–Act–Assert

  1. Arrange — create unit under test, prepare inputs, configure doubles
  2. Act — exactly one call. Multiple calls = multiple behaviors; split.
  3. Assert — verify observable result and side effects

Use // given / // when / // then comments only if the existing codebase uses them.

Mock Rules

  • Mock at boundaries — external APIs, databases, queues, filesystem, time, randomness
  • Don't mock internals — value objects, DTOs, pure functions use real instances
  • Mock your own port, not the third-party SDK — wrap external libraries behind an interface you control

Language References

If no reference exists for the project's language, apply the principles above and mirror existing project conventions.

ReferenceStack
javascript_unit_test.mdJest / Vitest
python_pytest_unit_test.mdpytest + monkeypatch / unittest.mock
java_unit_test.mdJUnit 5 + AssertJ + Mockito
go_std_unit_test.mdGo testing stdlib
go_testify_unit_test.mdGo + testify

Common Mistakes

MistakeFix
Asserting call counts when output would doAssert return value; mock only when the side effect IS the behavior
new Date(), Math.random(), real clockPin time, seed randomness — non-determinism breaks Repeatable
Mocking the third-party SDK directlyWrap in a port you own, mock the port
Silently copying broken patterns from existing testsFlag the anti-pattern to the user
.Maybe() / optional expectations on the calls that are the contractRegister every call explicitly; a test that passes whether or not the rollback happened verifies nothing
Boolean flags in the table that the loop body turns into mock expectationsPut the expectations in the case (a prepareMock closure or equivalent) so the reader sees exactly which calls the scenario makes
Following the neighbouring tests when the project names reference filesThe references win; neighbours may be legacy

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.