Skip to content
Back to skills

Test Driven Development

ASecurity

Use when implementing a feature or bugfix where a test is practical, before writing the code. Not for docs or config with no testable behavior.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added August 31, 2026
ai-agentsgotestingdebuggingrefactoringgit

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 27, 2026

npx -y skills add felipemelendez/llm-orchestrator --skill test-driven-development --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Driven Development?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test Driven Development
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/felipemelendez-test-driven-development/badge)](https://www.skillsdirectory.com/skills/felipemelendez-test-driven-development)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test-driven-development
description: Use when implementing a feature or bugfix where a test is practical, before writing the code. Not for docs or config with no testable behavior.
---

# Test-driven development

Write the test. Watch it fail. Write the least code that makes it pass.

**The load-bearing step is watching it fail.** A test you never saw fail has not been shown to test anything — it may assert nothing, never execute, mirror the implementation back at itself, or cover behavior that already worked, and a green suite hides all four. This is also why "write the code first, tests after" fails even when it feels equivalent: an after-the-fact test passes on its first run, which proves nothing, and it is shaped by the code that already exists — so it covers the cases you remembered, not the ones a test-first pass would have forced you to discover. Under deadline pressure, "tests after achieve the same purpose" is precisely the rationalization to refuse; the pressure is the reason for the discipline, not the exception to it.

## The loop

Red → verify red → green → verify green → refactor. One behavior per test — a name containing "and" is two tests — and name the behavior, not the function: `retries a failed call three times`, not `test retry`. Write against real code; reach for a mock only when the real dependency is slow or external, and read [writing-good-tests.md](./writing-good-tests.md) before adding one.

**Verify red** — the step that gets skipped. Run the test and confirm three things: it *failed* rather than errored (an import error or typo proves nothing about the behavior — fix and re-run until the failure is the assertion); the failure message is the one you predicted (if you can't say in advance what the failure will look like, you don't yet know what the test is for); and it failed because the behavior is missing, not because the test is wrong. If it *passed*, you're testing behavior that already exists — either the assertion is empty or this isn't the test you need. Passed or errored both mean fix the test, not the code.

**Green** — only enough code to pass the test you just watched fail. No options the test didn't ask for, no refactoring of neighbouring code, no improvements noticed along the way — separate cycles. But least code is not *narrowest* code: the test is one example of a rule, so write the rule, not a branch that recognises the example. `if n == 3: return True` passes and generalizes to nothing — a green line bought by moving the bug out of the test's line of sight. Same trap: special-casing the test's fixture path, returning the literal the assertion expects instead of computing it. Fewer options is the goal; fewer inputs handled correctly is not.

**Verify green** — run again: the test passes, every other test still passes, and the output is clean (no new warnings or stack traces that "don't matter"). If it still fails, fix the code — editing the assertion to match the code you wrote is how a suite stops meaning anything. If the test itself turns out wrong — it asserts something the spec doesn't ask for, or the task can't be done as written — say so and stop. A wrong test is a finding to report, not an obstacle to route around; every route around it (the edited assertion, the special case, the `skip`) leaves a suite that lies.

**Refactor** only once green: duplication, names, helpers. No new behavior, re-run after each step.

## Bug fixes

A bug is a missing test. Reproduce it as a failing test first, then fix; the test stops the bug returning, and the red run proves the test actually reproduces it. If the cause isn't obvious, invoke `systematic-debugging` first — find the cause, then write the test that captures it. A failing test you just wrote is the red phase, not a bug; don't send it back into debugging.

## When testing is hard

Difficulty testing is design feedback, not a reason to skip. Can't see how to test it → write the call you wish existed, then the assertion; if it still resists, the interface is unclear — a design finding. Enormous setup → the unit does too much or its dependencies aren't injectable. Mocking nearly everything → the code is tightly coupled, and mocking around it tests the mocks. Test more complex than the code → simplify the interface, not the test.

## When to relax

Docs, config, and formatting have no behavior to test. Throwaway exploration is fine — then delete it and start the real work with a test. A test that legitimately passes first because the behavior already worked happens — say so rather than deleting it. Relaxing is a decision stated out loud, not a default; "too simple to test," applied silently, is the exception swallowing the rule.

## Enforcement

Almost none. A Stop hook (`orch-verify-gate`) warns if a reply says `Verification: PASS` when no check ran and passed that turn. It never blocks. Nothing checks that you saw the test fail first, nothing compares a test file's history against the suite's, and nothing can tell a real red from a broken import — those were all removed with the machinery that guessed at them. The red phase is a discipline, not an enforced one, and skipping it is invisible.

## Output

Report both runs — that pair is what makes the cycle legible:

```
Verify:
- `pytest tests/test_retry.py -q` → 1 failed (before)
- `pytest tests/test_retry.py -q` → 1 passed (after)
```

If verification fails, the reply is `Found:` with the failure — not `Changed:`.

Files in this skill

  • SKILL.md5.7 KB
  • writing-good-tests.md5.9 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.