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.
Does it follow best practices?
Evaluation — 86%
↑ 1.05xAgent success when using this tile
Validation for skill structure
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();| 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 |
# Evaluate skill quality
sh skills/skill-quality-auditor/scripts/evaluate.sh test-driven-development --json# Lint this skill docs
bunx markdownlint-cli2 "skills/test-driven-development/**/*.md"Install with Tessl CLI
npx tessl i pantheon-ai/test-driven-development@0.2.4