| name | test-driven-development |
| description | Guides TDD (test-driven development) with red-green-refactor workflows, test-first feature delivery, bug reproduction through failing tests, behavior-focused assertions, and refactoring safety. Use when writing unit tests, implementing new functions, adding test coverage, fixing regressions, changing APIs, or restructuring code under test — especially when a user says "write tests first", "TDD", or "test before code". |
Test-Driven Development
Navigation hub for applying TDD in day-to-day implementation work.
When to Use
- "Write tests first for this feature."
- "How should I do red-green-refactor here?"
- "I need to reproduce a bug with a failing test first."
- "I want to refactor safely while preserving behavior."
When Not to Use
- End-to-end scenario design across full systems.
- Performance benchmarking and load testing.
- Security testing workflows.
Scope
In Scope
- Unit test design and behavior-driven assertions.
- Red-Green-Refactor cycle execution.
- Mock/stub isolation strategy.
- Naming and organization conventions for maintainable tests.
Out of Scope
- Full E2E harness setup and browser automation pipelines.
- Infra-only test framework bootstrapping.
- Non-deterministic perf profiling.
Workflow
- Write the smallest failing test that expresses one behavior.
- Run tests and verify failure is for the expected reason.
- Implement minimal code to make the test pass.
- Refactor while keeping the suite green.
- Repeat for next behavior/edge case.
Example: Red-Green-Refactor Cycle (TypeScript)
RED — write the failing test first:
import { add } from "./add";
test("add returns the sum of two numbers", () => {
expect(add(2, 3)).toBe(5);
});
GREEN — implement the minimum code to pass:
export function add(a: number, b: number): number {
return a + b;
}
REFACTOR — improve without breaking green:
export const add = (a: number, b: number): number => a + b;
Example: Elixir variant
# test/math_test.exs (RED)
test "add/2 returns the sum" do
assert Math.add(2, 3) == 5
end
# lib/math.ex (GREEN)
defmodule Math do
def add(a, b), do: a + b
end
Quick Commands
bun test
bun test --watch
bun test path/to/file.test.ts
mix test
Anti-Patterns
NEVER implement feature code before writing a failing test
WHY: skipping RED phase removes behavior-first design pressure.
export function greet(name: string) { return `Hello, ${name}!`; }
test("greet returns a personalised greeting", () => {
expect(greet("Ada")).toBe("Hello, Ada!");
});
NEVER test implementation details instead of behavior
WHY: implementation-coupled tests break during valid refactors.
expect(mockFormatter.format).toHaveBeenCalledBefore(mockLogger.log);
expect(result).toBe("Hello, Ada!");
NEVER combine multiple behaviors into one test case
WHY: failures become ambiguous and debugging slows down.
test("user lifecycle", () => { });
test("createUser returns a user with the given name", () => { ... });
test("login returns a session token for valid credentials", () => { ... });
NEVER use arbitrary sleeps in unit tests
WHY: fixed waits create flaky and slow suites.
await sleep(1000);
expect(result).toBeDefined();
await expect(promise).resolves.toBeDefined();
Verification
sh skills/agentic-harness/skill-quality-auditor/scripts/evaluate.sh test-driven-development --json
bunx markdownlint-cli2 "skills/test-driven-development/**/*.md"
References