Canon TDD
SkillAI & modelsImplement features and fix bugs test-first, the Canon TDD way (test list → one test → make it pass → refactor → repeat). Use when implementing any new feature, endpoint, model, or method, fixing a bug, or executing a plan — write the test list and a failing test BEFORE production code.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Canon TDD skill
What this skill tells your AI
The instructions your AI receives, as published by adamayoung/tmdb in .claude/skills/canon-tdd/SKILL.md and read by ahel’s review.
Test-driven development the way Kent Beck describes as "Canon TDD", per https://adam-young.co.uk/blog/canon-tdd/. CLAUDE.md mandates a TDD approach for this package; this skill is the detailed how.
Red-Green-Refactor is the engine. The Test List is the steering wheel.
Agent behaviour contract
The point of this skill: do these by default, without being reminded.
- Start with a test list, and show it. Before touching production code, enumerate the behaviours and edge cases as a checklist and confirm it with the user (or state it explicitly). This is the steering wheel — it defines "done".
- No production code before a failing test. Write exactly one test, run it, and watch it fail for the right reason. Never write the implementation first and back-fill tests.
- One test at a time. Do not convert the whole test list into code up front — the first passing test often forces a design change that affects the rest.
- Refactor only on green, and keep refactoring out of the make-it-pass step.
- Repeat until the test list is empty, adding newly-discovered cases to the list as you go.
The five steps
- Write a test list — every behavioural variant and edge case you can think of, as a plain checklist. Not code yet. It's a living list: add to it whenever implementation reveals a new case.
- Write a test — pick the next item and write one real, automated test: setup, invocation, and assertions. Define what the system does and how it's invoked — not how it works inside. Run it; confirm it fails — and state the failure: quote the failing assertion/message and check it is the expected reason. (For brand-new API the first red is normally a compile error — the symbol doesn't exist yet; that is expected. Stub the minimal declaration and re-run so the red becomes the failing assertion.) A compile error in existing code, a missing import, or an unrelated crash is a red for the wrong reason — it proves nothing about the behaviour, so fix the test and re-run until it is red for the right reason.
- Make it pass — change the system to satisfy that test with the simplest thing that works. Nothing more; resist solving items still on the list.
- Optionally refactor — improve names, structure, and duplication while every test stays green. Skip if there's nothing to improve.
- Repeat — next item, until the list is empty.
Separate interface from implementation
A test fixes a decision about the interface (the call shape, inputs, outputs) and should be silent about the internals. Tests that assert on private mechanics break under refactoring and defeat step 4.
Anti-patterns (don't)
- Tests without assertions — a test that invokes but asserts nothing proves nothing.
- Pre-converting the whole list to code — write one test, learn, then the next. Batching tests locks in design decisions before you've learned anything.
- Mixing refactor into make-it-pass — keep "make it work" and "make it clean" as separate steps on either side of green.
- Premature abstraction from duplication — a little duplication across two green tests is fine; abstract when the shape is clear, not at the first repeat.
In this codebase (TMDb)
Apply the loop with the project's tooling and conventions:
- Framework: Swift Testing —
@Suite,@Test,#expect,#require. Never force-unwrap in tests; unwrap optionals withtry #require(...). - Run tests via the delegated skills, not
makedirectly, so output stays out of context:/test(unit) and/integration-test(live API). Re-run after each red→green and after refactoring. - Both layers are required for new behaviour. Unit tests with JSON fixtures AND integration tests against the live API — put both kinds of items on the test list from the start.
- Models / new endpoints: when the test list involves decoding, the fixture
must exercise every decode branch (all N optional appended properties, plus
a paired "without appended data" test that asserts they're
nil). Use the TMDb MCP server (mcp__tmdb__*) to fetch real API responses for fixtures rather than inventing JSON — see.claude/docs/tmdb-api.md→ "Workflow for New Endpoints". - Bug fixes: the first item on the list is a test that reproduces the bug (red), then the fix (green) — never fix first.
- New public API: the test list should include the doc/DocC and README
updates as completion items, even though they aren't tests (per the
document-swiftskill's consistency checklist).
When NOT to use it
Pure refactors with existing coverage, formatting, config/CI edits, and docs-only changes don't start from a new failing test — keep the existing tests green instead. TDD is for new behaviour and bug fixes.
Signals
- GitHub stars
- 176
- Forks
- 47
- Last commit
- Aug 2026
Advanced
- Catalog kind
- skill
- Gateway key
canon-tdd- Source
- github.com/adamayoung/tmdb