Agent Skills: Test-Driven Development

Test-Driven Development workflows including red-green-refactor cycle, test-first implementation, outside-in vs inside-out testing, TDD for debugging, and TDD for refactoring. Use when implementing new features, refactoring existing code, using tests to drive design, or debugging with failing tests.

developmentID: randalmurphal/claude-config/test-driven-development

Install this agent skill to your local

pnpm dlx add-skill https://github.com/randalmurphal/claude-config/tree/HEAD/skills/test-driven-development

Skill Files

Browse the full folder contents for test-driven-development.

Download Skill

Loading file tree…

skills/test-driven-development/SKILL.md

Skill Metadata

Name
test-driven-development
Description
Use when implementing any feature, bug fix, or behavior change - before writing implementation code. Enforces strict RED-GREEN-REFACTOR cycle where tests are written and seen failing before any production code exists.

Test-Driven Development

Overview

Write the test first. Watch it fail. Write minimal code to pass.

Core principle: If you didn't watch the test fail, you don't know if it tests the right thing.

Violating the letter of the rules is violating the spirit of the rules.

When to Use

Always:

  • New features
  • Bug fixes
  • Refactoring
  • Behavior changes

Exceptions (ask first):

  • Throwaway prototypes
  • Generated code
  • Configuration files

The Iron Law

NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

Write code before the test? Delete it. Start over.

  • Don't keep it as "reference"
  • Don't "adapt" it while writing tests
  • Don't look at it
  • Delete means delete

Implement fresh from tests. Period.

Red-Green-Refactor

RED - Write Failing Test

Write one minimal test showing what should happen.

Requirements:

  • One behavior per test
  • Clear, descriptive name
  • Real code (no mocks unless unavoidable)

Good:

test('rejects empty email', () => {
  const result = submitForm({ email: '' });
  expect(result.error).toBe('Email required');
});

Bad:

test('form works', () => {    // vague name
  // tests 5 things at once   // too many behaviors
});

Verify RED - Watch It Fail

MANDATORY. Never skip.

Run the test. Confirm:

  • Test fails (not errors from typos)
  • Failure message matches expectation
  • Fails because the feature is missing

Test passes immediately? You're testing existing behavior. Fix the test.

GREEN - Minimal Code

Write the simplest code to make the test pass. Nothing more.

  • Don't add features beyond the test
  • Don't refactor other code
  • Don't "improve" beyond what the test requires

Verify GREEN - Watch It Pass

MANDATORY.

Run the test. Confirm:

  • Test passes
  • All other tests still pass
  • Output clean (no errors, warnings)

Test fails? Fix the code, not the test.

REFACTOR - Clean Up

After green only:

  • Remove duplication
  • Improve names
  • Extract helpers

Keep tests green. Don't add behavior.

Repeat

Next failing test for next behavior.

Why Order Matters

| Excuse | Reality | |--------|---------| | "I'll write tests after" | Tests passing immediately prove nothing | | "Too simple to test" | Simple code breaks. Test takes 30 seconds. | | "Already manually tested" | Ad-hoc != systematic. No record, can't re-run. | | "Deleting X hours is wasteful" | Sunk cost fallacy. Keeping unverified code is tech debt. | | "Keep as reference, write tests first" | You'll adapt it. That's testing after. Delete means delete. | | "Need to explore first" | Fine. Throw away exploration, start with TDD. | | "Test hard = design unclear" | Listen to the test. Hard to test = hard to use. | | "TDD slows me down" | TDD is faster than debugging. Always. | | "Tests after achieve same goals" | Tests-after = "what does this do?" Tests-first = "what should this do?" | | "It's about spirit not ritual" | Tests-after are biased by implementation. You test what you built, not what's required. | | "This is different because..." | No it isn't. Delete code. Start over with TDD. |

Red Flags - STOP and Start Over

If any of these are true, you skipped TDD:

  • Code written before test
  • Test written after implementation
  • Test passes immediately (never seen failing)
  • Can't explain why test failed
  • "I already manually tested it"
  • "Tests after achieve the same purpose"
  • "Keep as reference"
  • "Just this once"
  • "This is different because..."

All of these mean: Delete code. Start over with TDD.

Bug Fix Flow

  1. Write failing test reproducing the bug
  2. Watch it fail with the expected error
  3. Write minimal fix
  4. Watch it pass
  5. Run full suite - no regressions

Never fix bugs without a test.

When Stuck

| Problem | Solution | |---------|----------| | Don't know how to test | Write wished-for API. Write assertion first. | | Test too complicated | Design too complicated. Simplify the interface. | | Must mock everything | Code too coupled. Use dependency injection. | | Test setup huge | Extract helpers. Still complex? Simplify design. |

Testing Anti-Patterns

Watch for these:

  • Testing mock behavior instead of real behavior - if your assertion is about the mock, you're testing the mock
  • Test-only methods in production - move cleanup logic to test utilities
  • Mocking without understanding - know what side effects matter before mocking
  • Incomplete mocks - partial mock structures create silent failures
  • Mock setup > 50% of test - you're probably mocking too much

Verification Checklist

Before marking work complete:

  • [ ] Every new function/method has a test
  • [ ] Watched each test fail before implementing
  • [ ] Each test failed for expected reason (feature missing, not typo)
  • [ ] Wrote minimal code to pass each test
  • [ ] All tests pass
  • [ ] Output clean (no errors, warnings)
  • [ ] Tests use real code (mocks only if unavoidable)
  • [ ] Edge cases and errors covered

Can't check all boxes? You skipped TDD. Start over.

Final Rule

Production code -> test exists and failed first
Otherwise -> not TDD

No exceptions without explicit permission.