MCPInstall

writing-unit-tests

Use when writing or refactoring unit tests - a new test file, added cases, a flaky test, an unreadable one. Encodes behavior-first testing: one behavior per test, names that state the claim, deterministic setup, mocks only at boundaries you do not own. Use whenever a test is being written, even a quick one, and whenever a test needs a sleep, a mock of your own code, or a copy of the implementation's math.

Installs
18.4K
Last 24h
+389
Trending rank
#790
Security
Pass

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/riekelt/principal-engineer --skill writing-unit-tests
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads writing-unit-tests automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

Security audits

Independent scans of the skill's instructions and bundled files.

Gen Agent Trust Hub PassSAFE ·The skill provides guidelines and best practices for writing high-quality unit tests. It contains only documentation and behavioral instructions for the AI agent, with no executable code, network requests, or sensitive data access detected.
Socket PassNo alerts
Snyk PassLOW ·Risk: LOW · No issues

SKILL.md

The instructions your agent reads when the skill is triggered.

Writing unit tests

REQUIRED BACKGROUND: the principal-engineering skill. testing-changes governs which tests a change owes; this skill is the craft of the tests themselves.

Overview

A unit test is a behavioral claim with a name, read by the next engineer during a red build. Core principle: test the contract, not the implementation. The name carries the claim, and the test stays simple enough that it cannot itself be wrong.

Contract over implementation

  • Test through the public contract of the unit. A refactor that preserves behavior should not break tests; when it does, the tests were asserting the implementation.
  • Do not assert call sequences, internal state, or that method A called method B, unless the interaction IS the contract (a required side effect on a boundary).
  • Never derive the expected value from the production arithmetic, neither by reimplementing the formula nor by invoking the shared helper that computes it. Expected values are literals worked out independently (by hand, from a spec, from real data), with the derivation in a comment.

One behavior per test, named as the claim

  • One behavior per test; splitting is cheaper than archaeology on a multi-assert failure.
  • The name states subject, scenario, and expected outcome: expired_token_is_rejected_with_401, not test_auth_3. Test names describe behavior, state transitions, and invariants; never delivery order, ticket keys, or phases.
  • Arrange, act, assert, visibly and in that order. No branching, loops, or logic in a test: a test with logic needs its own test. Shared setup earns a builder or a role-named fixture; a mystery blob fixture hides which arranged fact the assertion depends on. Generation and iteration live in builders and helpers, not in the test body. Property-based tests are the accepted form for invariants and follow their framework's shape; example-based tests stay logic-free.

Determinism

  • No real time, real randomness, real network, or real filesystem inside a unit test: inject the clock, seed or inject the randomness, mock the boundary.
  • No sleeps. Waiting for async work is condition-based (poll the observable outcome with a deadline), never duration-based.
  • A flaky test is red: fix it or quarantine it visibly with an owner (see the red-test rule in testing-changes); re-running until green is silencing a detector.

Mocks are assumptions

  • Mock the boundaries you do not own (network, clock, filesystem, third-party services); prefer real collaborators for code you do own within the unit's reach. For owned wrappers around unowned resources (your repository class fronting the database), mock at the seam where owned code last touches the unowned resource, and keep the test data role-named and visible either way. Every mock hardcodes an assumption about a contract; a stale mock is how a suite stays green while the real integration is broken.
  • When a test is mostly mock wiring, it is testing the mocks. Either widen the unit to something with real behavior or accept that this seam needs an integration test instead (and say which).
  • Fixtures are labeled snapshots of reality: minimal, role-named for their part in the scenario, updated deliberately when the contract changes, never regenerated blindly to make red go green.

Assertions and failure paths

  • Assert outcomes with values, not absence of exceptions. "It did not throw" claims almost nothing.
  • Failure paths are first-class test subjects: the typed failure surfaces, the degraded mode is entered loudly, the guard actually guards (see handling-failures).
  • Tests themselves follow the no-silent-swallows contract: no catch-and-ignore in test code, no conditional assertions that skip silently when a precondition is absent. A test that cannot run must fail or be visibly skipped with the reason.

Common mistakes

  • The mirror test: reimplementing the production logic to compute the expectation.
  • The mock echo chamber: mocking your own class and asserting the mock.
  • The mega-test: twelve assertions, one name, no way to know which claim broke.
  • Shared mutable fixtures that make test order matter; every test builds or receives its own state.
  • The sleep that "fixes" flakiness by making it rarer.
  • Green-checking the fixture: editing expected values to match actual output without deriving why the new value is right.

FAQ

What does the writing-unit-tests skill do?

Use when writing or refactoring unit tests - a new test file, added cases, a flaky test, an unreadable one. Encodes behavior-first testing: one behavior per test, names that state the claim, deterministic setup, mocks only at boundaries you do not own. Use whenever a test is being written, even a quick one, and whenever a test needs a sleep, a mock of your own code, or a copy of the implementation's math.

How do I install writing-unit-tests?

Run npx skills add https://github.com/riekelt/principal-engineer --skill writing-unit-tests in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/writing-unit-tests/ (personal) or .claude/skills/writing-unit-tests/ (project).

Which AI agents can use writing-unit-tests?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is writing-unit-tests safe to install?

Security partners have reviewed it: Gen Agent Trust Hub passed; Socket passed; Snyk passed. As with any skill, read the SKILL.md and bundled scripts before installing.

Who maintains writing-unit-tests?

It is published by riekelt in riekelt/principal-engineer. It has 18,387 recorded installs.

More from riekelt/principal-engineer

View all →

principal-engineering

riekelt/principal-engineer

Use when doing any non-trivial engineering work - implementing, debugging, refactoring, configuring, operating, or investigating why a system misbehaves - or any change where being wrong has a cost. Encodes the evidence-over-theory discipline, the hard safety rules, and the pre-change checkpoint. Use whenever code, data, or infrastructure is about to change or must be understood before it can, even if the task looks routine or is only "find out why". Foundation for the sibling skills.

verifying-before-done

riekelt/principal-engineer

Use when about to say "done", "fixed", "passing", or "shipped", or when reporting the outcome of any change. Encodes verification as the definition of done: drive the change at its surface, run the verify command, report faithfully, distrust green suites, own failing gates. Use before every completion claim, even when the change was small and obviously correct, which is when this is skipped.

Docs & Writing18.4K installs

guarding-architecture

riekelt/principal-engineer

Use when a change crosses module boundaries, adds a dependency direction between modules, touches a critical path, or conflicts with a stated principle - and when writing or updating architecture principles themselves. Encodes structural invariants as named, enforced contracts: statement, rationale, guard. Use whenever "we'll just import it from there for now" appears, which is how boundaries die.

keeping-one-source-of-truth

riekelt/principal-engineer

Use when adding data, config, state, constants, an enum-like string, a cache, or anything that could exist in two places - or when two sources already disagree. Encodes the one-fact-one-source doctrine for code and data: derive rather than store, extend the owner, absorb duplicates. Use at the moment copying a value feels faster than referencing it.

18.3K installs

testing-changes

riekelt/principal-engineer

Use when deciding what tests a change needs - a feature, a bug fix, a refactor, any behavior change - or when reviewing whether a diff's tests are sufficient. Encodes tests-change-with-behavior, the bug-regression pattern, and assertion discrimination. Use whenever behavior changes and the test diff is empty, especially when the change is "too small to test".

Testing & QA18.3K installs

grounding-before-coding

riekelt/principal-engineer

Use when starting any non-trivial change, investigating a bug, or working in unfamiliar code - before the first line is written. Also use for pure investigation with no change planned yet - "dig into this", "figure out why", "sometimes the export is empty", intermittent errors after a deploy. Encodes the ground-first discipline: map the real code and data, quote evidence, never guess conventions. Use whenever a change or a conclusion is about to be built from belief instead of from the tree, even under time pressure.

DevOps & Cloud18.3K installs

Popular testing & qa skills

View category →

tdd

mattpocock/skills

Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.

Testing & QA1.2M installs

grilling

mattpocock/skills

Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking, or uses any 'grill' trigger phrases.

Testing & QA996.2K installs

migrate-to-shoehorn

mattpocock/skills

Migrate test files from as type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace as in tests, or needs partial test data.

Testing & QA484.1K installs

run-train

lllllllama/rigorpilot-skills

Rigor Train skill for deep learning research repositories. Use when a documented or selected training command should be run conservatively for startup verification, short-run verification, full kickoff, or resume, with command, config, seed, log, checkpoint, status, and metric evidence written to standardized trainoutputs/. Do not use for environment setup, exploratory sweeps, speculative idea implementation, or end-to-end orchestration.

Testing & QA337.8K installs

test-driven-development

obra/superpowers

Use when implementing any feature or bugfix, before writing implementation code

Testing & QA261.8K installs

Use when implementation is complete, all tests pass, and you need to decide how to integrate the work

Testing & QA216.8K installs