← Back to list
🛠️

test-driven-development Checked

Use when implementing any feature or bugfix, before writing implementation code
🛠️ Developer Tools skills By obra Version v1.0.5bf4e78 Added 2026-09-07 Updated 2026-09-22 Source ↗ Part of Developer Tools →
9.1Overall rating
2Views
16Downloads
282.5kStars

Installation

🤖 Install via AI

Copy the prompt below and send it to your AI assistant (e.g. Claude Code) — it will follow the instructions and install automatically.

Install the "test-driven-development" skill by following the instructions at https://skill123.me/install/test-driven-development.
⌨️ Command line install

Run in your terminal — downloads and installs to ~/.claude/skills/.

curl -fsSL https://skill123.me/install/test-driven-development.sh | bash
📦 Download ZIP

Download the zip and extract it into your skills directory (e.g. ~/.claude/skills/), then restart your session.

⬇ Download v1.0.5bf4e78 · 8 KB
📊 Static rating
9.1/10
Trigger
8.5
Structure
8.0
Workflow
9.2
Content
9.0
Engineering
10.0
Security
10.0

Six-dimension rubric with reviewer rationale.

View scorecard ↓
🛡 Dynamic test
—

Not live-tested yet — this skill is rated on static analysis only.

About this skill

Use when implementing any feature or bugfix, before writing implementation code

Test-Driven Development (TDD)

Documentation

Use when implementing any feature or bugfix, before writing implementation code

Test-Driven Development (TDD)

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 your human partner):**

  • Throwaway prototypes
  • Generated code
  • Configuration files

Thinking "skip TDD just this once"? Stop. That's rationalization.

The Iron Law


NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

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

**No exceptions:**

  • 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


digraph tdd_cycle {
    rankdir=LR;
    red [label="RED\nWrite failing test", shape=box, style=filled, fillcolor="#ffcccc"];
    verify_red [label="Verify fails\ncorrectly", shape=diamond];
    green [label="GREEN\nMinimal code", shape=box, style=filled, fillcolor="#ccffcc"];
    verify_green [label="Verify passes\nAll green", shape=diamond];
    refactor [label="REFACTOR\nClean up", shape=box, style=filled, fillcolor="#ccccff"];
    next [label="Next", shape=ellipse];

    red -> verify_red;
    verify_red -> green [label="yes"];
    verify_red -> red [label="wrong\nfailure"];
    green -> verify_green;
    verify_green -> refactor [label="yes"];
    verify_green -> green [label="no"];
    refactor -> verify_green [label="stay\ngreen"];
    verify_green -> next;
    next -> red;
}

### RED - Write Failing Test

Write one minimal test showing what should happen.

<Good>


test('retries failed operations 3 times', async () => {
  let attempts = 0;
  const operation = () => {
    attempts++;
    if (attempts < 3) throw new Error('fail');
    return 'success';
  };

  const result = await retryOperation(operation);

  expect(result).toBe('success');
  expect(attempts).toBe(3);
});

Clear name, tests real behavior, one thing

</Good>

<Bad>


test('retry works', async () => {
  const mock = jest.fn()
    .mockRejectedValueOnce(new Error())
    .mockRejectedValueOnce(new Error())
    .mockResolvedValueOnce('success');
  await retryOperation(mock);
  expect(mock).toHaveBeenCalledTimes(3);
});

Vague name, tests mock not code

</Bad>

**Requirements:**

  • One behavior
  • Clear name
  • Real code (no mocks unless unavoidable)

### Verify RED - Watch It Fail

**MANDATORY. Never skip.**


npm test path/to/test.test.ts

Confirm:

  • Test fails (not errors)
  • Failure message is expected
  • Fails because feature missing (not typos)

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

**Test errors?** Fix error, re-run until it fails correctly.

### GREEN - Minimal Code

View full documentation

Write simplest code to pass the test.

<Good>


async function retryOperation<T>(fn: () => Promise<T>): Promise<T> {
  for (let i = 0; i < 3; i++) {
    try {
      return await fn();
    } catch (e) {
      if (i === 2) throw e;
    }
  }
  throw new Error('unreachable');
}

Just enough to pass

</Good>

<Bad>


async function retryOperation<T>(
  fn: () => Promise<T>,
  options?: {
    maxRetries?: number;
    backoff?: 'linear' | 'exponential';
    onRetry?: (attempt: number) => void;
  }
): Promise<T> {
  // YAGNI
}

Over-engineered

</Bad>

Don't add features, refactor other code, or "improve" beyond the test.

### Verify GREEN - Watch It Pass

**MANDATORY.**


npm test path/to/test.test.ts

Confirm:

  • Test passes
  • Other tests still pass
  • Output pristine (no errors, warnings)

**Test fails?** Fix code, not test.

**Other tests fail?** Fix now.

### 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 feature.

Good Tests

| Quality | Good | Bad |

|---------|------|-----|

| **Minimal** | One thing. "and" in name? Split it. | test('validates email and domain and whitespace') |

| **Clear** | Name describes behavior | test('test1') |

| **Shows intent** | Demonstrates desired API | Obscures what code should do |

When writing or changing any test, read writing-good-tests.md (writing-good-tests.md) for the rules that keep tests honest:

  • Name the production change that would make the test fail — before writing it
  • Assert on real behavior, never on mock behavior
  • Keep test-only code in test utilities, out of production classes
  • Understand a dependency's side effects before mocking it

Common Rationalizations

| Excuse | Reality |

|--------|---------|

| "Too simple to test" | Simple code breaks. Test takes 30 seconds. |

| "I'll test after" | Tests written after pass immediately — which proves nothing. They may test the wrong thing, test the implementation instead of the behavior, or miss the edge case you forgot. You never watched it fail, so you never proved it can catch the bug. Test-first forces that failure. |

| "Tests after achieve same goals (spirit not ritual)" | Tests-after answer "what does this do?"; tests-first answer "what should this do?" Tests written after are biased by the code you already wrote — you verify the cases you remembered, not the ones you'd have discovered. Coverage without proof the tests work. |

| "Already manually tested" | Manual testing is ad-hoc: no record of what you covered, no way to re-run it when the code changes, easy to forget cases under pressure. "Worked when I tried it" ≠ comprehensive. Automated tests run the same way every time. |

| "Deleting X hours is wasteful" | Sunk cost fallacy — that time is already spent either way. The real choice: rewrite with TDD (high confidence) vs. keep it and bolt tests on after (low confidence, likely bugs). Keeping code you can't trust is the waste. |

| "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 test. Hard to test = hard to use. |

| "TDD will slow me down" | TDD IS the pragmatic path: catches bugs before commit, prevents regressions, lets you refactor without fear. "Pragmatic" shortcuts mean debugging in production — slower, not faster. |

| "Manual test faster" | Manual doesn't prove edge cases. You'll re-test every change. |

| "Existing code has no tests" | You're improving it. Add tests for existing code. |

Red Flags - STOP and Start Over

  • Code before test
  • Test after implementation
  • Test passes immediately
  • Can't explain why test failed
  • Tests added "later"
  • Rationalizing "just this once"
  • "I already manually tested it"
  • "Tests after achieve the same purpose"
  • "It's about spirit not ritual"
  • "Keep as reference" or "adapt existing code"
  • "Already spent X hours, deleting is wasteful"
  • "TDD is dogmatic, I'm being pragmatic"
  • "This is different because..."

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

Example: Bug Fix

**Bug:** Empty email accepted

**RED**


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

**Verify RED**


$ npm test
FAIL: expected 'Email required', got undefined

**GREEN**


function submitForm(data: FormData) {
  if (!data.email?.trim()) {
    return { error: 'Email required' };
  }
  // ...
}

**Verify GREEN**


$ npm test
PASS

**REFACTOR**

Extract validation for multiple fields if needed.

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
  • [

Score breakdown

Trigger
8.5
Precise Use-when covering features and bugfixes, with explicit exceptions listed in body.
Structure
8.0
320-line SKILL.md plus writing-good-tests.md at root level; content otherwise well staged.
Workflow
9.2
Complete TDD cycle with mandatory verify-RED and verify-GREEN gates, stuck-table, checklist and debugging integration.
Content
9.0
Sharp imperative writing with good/bad test contrasts and a full worked example.
Engineering
10.0
Valid frontmatter; writing-good-tests.md reference exists in package.
Security
10.0
Pure methodology text; no scripts, network, secrets, or injection.
Model discipline skill: Iron Law, full RED-GREEN-REFACTOR cycle with mandatory fail/pass verification, rationalization and red-flag tables, worked bugfix example and completion checklist; supporting doc sits at root rather than references/.

Assessment methodology

🧪
AI-assessed
Evaluated by automated analysis across quality, usability, and security dimensions. This is not a substitute for your own review — always inspect a skill before installing it.

Version history

v1.0.5bf4e78 2026-09-22
8 KB

— Refreshed from GitHub obra/superpowers@5bf4e78

v1.0.0 2026-09-07
8 KB

Imported from obra/superpowers@main