skills/writing-tests/SKILL.md
Writes and runs a test suite for a piece of code, covering happy path, edge cases, error cases, and security cases. Use when: implementation is complete and needs test coverage, a bug needs a reproduction test and fix validation, or code needs coverage before a refactor. Do not use when: the code under test is not yet implemented, or the spec is still unclear.
npx skillsauth add maestria-co/ai-playbook writing-testsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Validate that code behaves correctly across the full range of inputs and conditions. Tests verify the contract (observable behavior), not the internals (implementation details). Tests coupled to internals break on refactors and create false confidence.
Think before writing. Read the spec and implementation first. Identify all the ways the code can fail before writing a single test.
Check 1 — Read the implementation and spec
Read both the spec/acceptance criteria and the implementation code before writing tests.
Check 2 — Find the test framework and conventions
Locate 2–3 existing test files in the same area. Identify:
Test framework and runner (Jest, Vitest, pytest, etc.)
Folder structure and naming convention (*.test.ts, *.spec.js, tests/)
How mocks and fixtures are set up
How async code is tested
Write new tests that match these conventions exactly.
If no tests exist in the codebase → use the framework in the project manifest (package.json, etc.).
Write a comment block listing every test case you plan to write, grouped by category:
// Happy path: valid input → expected output
// Edge cases: null, empty, boundary values, special chars
// Error cases: invalid input, missing required fields, downstream failures
// State cases: before/after state transitions, concurrent writes
// Security cases: [only if code handles auth, external input, or file I/O]
// - SQL injection / NoSQL injection
// - Malformed or oversized inputs
// - Missing/expired/tampered tokens
Verify that the code does what it's supposed to do for valid, expected input. At minimum: one test per distinct success path.
Cover boundary conditions:
"", [], {}, null, undefined)Verify that failures are handled explicitly and produce correct error behavior:
For code that handles authentication, authorization, or external input:
Run tests with the no-watch flag:
--watchAll=false or --runpytest (no watch by default)Coverage targets:
New code: ≥90%
Bug fixes: must include a test that reproduces the bug and passes after the fix
Refactors: coverage must not decrease from before the refactor
If all tests pass and coverage is met → proceed to output.
If tests fail → diagnose and fix tests (not the implementation, unless the implementation is wrong).
If code is untestable (global state, no dependency injection) → report it. Do not mock the thing under test.
Produce a test report.
All tests passing:
Tests: ✅ All passing
Coverage: [X%] | Tests: N (unit: N, integration: N)
Files: [test files written or modified]
Suggested next: [next task in plan, or "done"]
Tests failing or bugs found:
Tests: ⚠️ N failing
Bugs:
- Bug: [description] | File: [path:line] | Input: [reproduction] | Expected: [value] | Actual: [value] | Severity: [low/medium/high]
Suggested next: implementing-features (fix bugs, then return to writing-tests)
Code untestable:
Tests: ❌ Blocked
Reason: [e.g., global state in src/services/auth.js:47, no dependency injection]
Suggested next: implementing-features (refactor for testability, then return to writing-tests)
Test cases covered: Happy path: create valid OAuthProvider, retrieve by userId+provider Edge cases: null userId, empty provider string, token at max length Error cases: duplicate (userId+provider) raises unique constraint, invalid provider value Security cases: accessToken encrypted at rest (stored value differs from plaintext input) </output> </example>
<example> <scenario>Writing a regression test for a bug fix</scenario> <input> <task>Add regression test for the case-sensitive email bug fix.</task> <context> - Bug: login failed for mixed-case emails (e.g., [email protected]) - Fix applied in src/services/auth.js:47 — .toLowerCase() added before DB lookup - Existing tests: tests/services/auth.test.js </context> </input> <output> Tests: ✅ All passing Coverage: 91% (unchanged) | Tests: 8 (7 existing + 1 new) Files: tests/services/auth.test.js (added 1 test)Added test: "login succeeds for mixed-case email"
Suggested next: implementing-features — refactor tokenRefresh.js to accept a redis client as a constructor/function parameter, then return to writing-tests. </output> </example>
</examples>implementing-featurestesting
Use when creating a new skill, editing an existing skill, or helping a user author a skill for this system. Covers structure, discoverability, quality, and discipline hardening.
development
Evidence-based verification process to run before marking any task complete. Use this skill every time you're about to report that work is done — for features, bug fixes, refactoring, or any code change. This catches the most common failure mode: declaring "done" without proof. If you're finishing up and about to tell the user the task is complete, run this checklist first.
development
Teaches agents how to discover, select, and invoke skills from the skill library. Use this skill whenever you're uncertain which skill applies to a task, when composing multiple skills for complex work, or when you need to understand what skills are available. This is your go-to when facing an ambiguous task and need to figure out the right approach before diving into implementation.
development
Safely upgrade a language version, framework, or major dependency. Use when someone says "upgrade Node to v22", "migrate to React 19", "we need to get off this old version", "update our framework", or "how do we upgrade X?". Also triggers when a security advisory requires a version bump. Scope is infrastructure only: manifests, lock files, build configuration, and source changes required by the new version. Content drift discovered during an upgrade (outdated docs, stale patterns) is out of scope — delegate those to `context-maintenance`. Prevents the most common upgrade pitfalls: skipping migration guides, multi-major jumps, and losing rollback options.