Use when deciding which tests to write for a change, how to name and structure them, what data they use, and how deep to go — and when an existing test is flaky, asserts nothing meaningful, or breaks on every refactor.
Installs into .claude/skills of the current project.
Are you the author of Dev Testing Craft?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/connorpham-dev-testing-craft)
---
name: dev-testing-craft
description: "Use when deciding which tests to write for a change, how to name and structure them, what data they use, and how deep to go — and when an existing test is flaky, asserts nothing meaningful, or breaks on every refactor."
---
# Testing craft — tests are the executable spec, written to fail first
## Identity
You write tests that a stranger can read as requirements, that fail for exactly
one reason, that never pass by accident, and that survive a refactor of the
implementation. You test behavior through the interface with realistic data;
you put the effort where a failure hurts most; you write the boundary pair for
every rule. A test that has never been red has proven nothing.
## When this applies
- Before implementing any behavior change (the test comes first).
- Choosing between unit, integration and end-to-end for a given check.
- Writing test names, fixtures, assertions.
- A test is flaky, slow, or asserts `toBeTruthy`.
## Decide
| Question | Choose | Because |
|---|---|---|
| Which level? | Unit for logic; integration (real DB/HTTP in-process) for wiring; e2e only for the few lifeline journeys | Cost rises 10× per level; confidence per test does not |
| How many, how deep? | Risk = probability × impact; P0 (money, auth, data loss) gets the boundary table and an e2e; P3 gets a smoke | Equal effort everywhere means too little where it matters |
| What to assert? | The observable outcome the spec names, with its exact value cited (`spec §3.2`, `schema Lot.status`) | Asserting internals couples the test to the implementation |
| Test data | Realistic values, generated per test, unique | `"foo"` never hits the length limit; shared fixtures couple tests |
| Boundary | Both sides: the last valid and the first invalid (`limit`, `limit+1`; `0`, `-1`; empty, one, many) | Off-by-one lives exactly there |
| Doubles | Fake at the seam (in-memory adapter); mock only what you cannot own (payment provider, clock) | Mocks of your own code test the mock |
| Flaky | Fix the nondeterminism (time, order, shared state, network) — never retry-until-green | Retries hide the bug the test found |
## Rules
- **Name = unit · scenario · expected**: *"topUp — when amount exceeds daily
limit — rejects with LIMIT_EXCEEDED and leaves balance unchanged"*.
*Otherwise:* a red CI says "test 14 failed" and nobody knows what broke.
- **AAA, visibly**: Arrange, Act, Assert; one act per test; assertions in the
test body, not hidden in a helper. *Otherwise:* the reader cannot tell what is
being claimed.
- **Red first, for the right reason.** Run it; read the failure; only then make
it green. *Otherwise:* a test that passed immediately may be testing nothing.
- **Black-box through the public interface.** *Otherwise:* every refactor
breaks tests that were never about behavior.
- **Every rule gets its boundary pair; every write gets a read-back**, ideally
through a different path (API write, DB read). *Otherwise:* the write "succeeds"
and persists nothing.
- **Determinism**: no sleeps, no wall clock, no test-order dependence, no
conditionals in the test body. *Otherwise:* the suite becomes a coin flip and
gets ignored.
- **Isolation**: each test creates what it needs and cleans up; parallel-safe.
*Otherwise:* a green suite locally is red in CI, or vice versa.
- **Don't test the framework or the mock.** *Otherwise:* thousands of lines of
false confidence.
## Reviewer lens
- For each rule in the spec touched by the diff: where is the pair (last valid, first invalid)?
- Flip one condition in the implementation; which test reds? None → say so.
- Any assertion that would pass for a wrong answer? Any test that never ran red?
## Sources
goldbergyoni/javascript-testing-best-practices (3-part names, AAA, black-box,
realistic data) · BMAD TEA test-quality standards (determinism, isolation,
explicit assertions) and risk-based P0–P3 · obra/superpowers
`test-driven-development` (red first, rationalizations).
Rationalizations, red flags and a worked example live in `reference/dev-testing-craft.md` — opened when needed, never loaded by default.