Master Test-Driven Development with deterministic red-green-refactor workflows, test-first feature delivery, bug reproduction through failing tests, behavior-focused assertions, and refactoring safety; use when implementing new functions, changing APIs, fixing regressions, or restructuring code under test.
71
89%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Navigation hub for applying TDD in day-to-day implementation work.
RED — write the failing test first:
// add.test.ts
import { add } from "./add";
test("add returns the sum of two numbers", () => {
expect(add(2, 3)).toBe(5);
});
// ❌ Fails: cannot find module './add'GREEN — implement the minimum code to pass:
// add.ts
export function add(a: number, b: number): number {
return a + b;
}
// ✅ Test passesREFACTOR — improve without breaking green:
// add.ts — no logic change needed here, but you might rename,
// extract constants, or improve types while the suite stays green.
export const add = (a: number, b: number): number => a + b;
// ✅ Still green# 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# Run full suite
bun test# Watch mode while iterating
bun test --watch# Run a specific file
bun test path/to/file.test.ts# Elixir variant
mix testWHY: skipping RED phase removes behavior-first design pressure.
// BAD: production code written first, test added after
export function greet(name: string) { return `Hello, ${name}!`; }
// ... then test written to match existing impl
// GOOD: test written first, impl follows
test("greet returns a personalised greeting", () => {
expect(greet("Ada")).toBe("Hello, Ada!");
});
// ❌ red → write greet() → ✅ greenWHY: implementation-coupled tests break during valid refactors.
// BAD: asserting internal call sequence
expect(mockFormatter.format).toHaveBeenCalledBefore(mockLogger.log);
// GOOD: assert observable output
expect(result).toBe("Hello, Ada!");WHY: failures become ambiguous and debugging slows down.
// BAD: one test covers create/login/update/delete flow
test("user lifecycle", () => { /* 40 lines */ });
// GOOD: one behavior per test
test("createUser returns a user with the given name", () => { ... });
test("login returns a session token for valid credentials", () => { ... });WHY: fixed waits create flaky and slow suites.
// BAD
await sleep(1000);
expect(result).toBeDefined();
// GOOD: synchronize with deterministic signals
await expect(promise).resolves.toBeDefined();# Evaluate skill quality
sh skills/agentic-harness/skill-quality-auditor/scripts/evaluate.sh test-driven-development --json# Lint this skill docs
bunx markdownlint-cli2 "skills/test-driven-development/**/*.md"| Topic | Reference |
|---|---|
| Red-Green-Refactor cycle | references/cycle-write-test-first.md |
| Verify failing tests first | references/cycle-verify-test-fails-first.md |
| AAA and behavior-first design | references/design-aaa-pattern.md |
| Avoid implementation-detail tests | references/design-test-behavior-not-implementation.md |
| Isolation and dependency injection | references/isolate-use-dependency-injection.md |
| Flakiness and speed | references/perf-fix-flaky-tests.md |