PluginBench
Skill
Pass
Audit score 90

test-driven-development

sickn33/agentic-awesome-skills

Write failing tests first to guide implementation, then refactor with confidence.

What is test-driven-development?

Test-Driven Development (TDD) is a disciplined approach where you write a failing test before implementing a feature or fix. Follow the Red-Green-Refactor cycle: write a minimal failing test, verify it fails for the right reason, implement minimal code to pass, then refactor while keeping tests green. Use this when behavior changes need repeatable verification and regression protection.

  • Write minimal failing tests that demonstrate required behavior before implementation
  • Verify tests fail for expected reasons, not typos or setup issues
  • Implement minimal code to pass each test without over-engineering
  • Refactor safely while keeping all tests passing
  • Catch regressions and prevent defects from reoccurring

How to install test-driven-development

npx skills add https://github.com/sickn33/agentic-awesome-skills --skill test-driven-development
Prerequisites
  • A working test runner (Jest, Vitest, Mocha, etc.) in your repository
  • Understanding of your codebase's testing conventions and fixtures
  • Ability to run tests locally and interpret failure messages
Claude Code
Cursor
Windsurf
Cline

How to use test-driven-development

  1. 1.Write one minimal test describing the desired behavior with a clear name
  2. 2.Run the test and verify it fails for the expected reason (not setup errors)
  3. 3.Write the simplest code to make the test pass without adding extra features
  4. 4.Run all tests to confirm the new test passes and no regressions occur
  5. 5.Refactor to remove duplication and improve names while keeping tests green
  6. 6.Repeat for the next behavior or edge case

Use cases

Good for
  • Fix a bug by writing a regression test that reproduces the defect, then implement the minimal fix
  • Add a new feature by writing the test for desired behavior first, then implement to pass
  • Validate edge cases and error handling with focused tests before writing production code
  • Refactor existing code with confidence by ensuring regression tests stay green
  • Document intended API behavior through clear, executable test examples
Who it's for
  • Backend and frontend developers implementing features or fixes
  • Teams adopting quality practices and regression prevention
  • Developers working on codebases with existing test runners
  • Anyone needing to prove a fix works and prevent future breakage

test-driven-development FAQ

What if the test passes on the first run?

Determine whether it already characterizes the required behavior. For a regression test, prove it detects the defect using the prior version or a controlled change. Do not alter a correct assertion just to force a failure.

Should I mock external dependencies in my tests?

Use real code and real boundaries where practical. Mocks are useful only to isolate external dependencies while preserving the contract under test. If you must mock everything, the design is too coupled; consider dependency injection instead.

Can I write tests after implementation?

Yes, but a test written after a fix is less sensitive to the original defect. Always verify that a regression test fails on the buggy code and passes after the fix. Report honestly whether the test preceded the implementation.

What if my test is too complicated to write?

The test complexity signals design complexity. Simplify the interface or API first. Write the wished-for API, then the assertion. If setup is huge, extract helpers or reconsider the design.

How do I know when to stop refactoring?

Refactor only after the test passes. Stop when the code is clear, duplication is removed, and all tests remain green. Do not add new behavior during refactoring.

Full instructions (SKILL.md)

Source of truth, from sickn33/agentic-awesome-skills.


name: test-driven-development description: "Use a failing behavioral test to guide a feature or bug fix, then implement and refactor with relevant regression checks." risk: critical source: community date_added: "2026-02-27"

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

Use for behavior changes where a repeatable test can demonstrate the requirement or reproduce the bug. Inspect the repository’s test runner and existing coverage first. For copy, generated outputs or low-impact configuration, use the appropriate focused validation rather than manufacturing a unit test.

Preserve existing work

Write the failing regression before the repair when feasible, and verify that it fails for the expected reason. If implementation already exists, preserve it and add characterization/regression tests. Do not delete user work, reset a branch or rewrite working code to reconstruct an ideal test-first history. State honestly whether the test preceded the fix.

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> ```typescript test('succeeds on the third attempt', async () => { let attempts = 0; const operation = async () => { 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>
```typescript
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? Determine whether it already characterizes the required behavior. For a regression, prove it detects the defect using the prior revision or an isolated controlled change; do not alter a correct assertion just to force red.

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

GREEN - Minimal Code

Write simplest code to pass the test.

<Good> ```typescript 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> ```typescript 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

QualityGoodBad
MinimalOne thing. "and" in name? Split it.test('validates email and domain and whitespace')
ClearName describes behaviortest('test1')
Shows intentDemonstrates desired APIObscures what code should do

Why order matters

A failing test can expose a misunderstood requirement before implementation. A test written after a fix can still be valuable, but its sensitivity to the original defect needs evidence. Neither timing nor coverage percentage proves the assertion is meaningful.

If a failure is caused by a missing import, unavailable service or bad fixture, repair that setup before interpreting the result. Use real boundaries where practical; a mock is useful when it isolates an external dependency while preserving the contract under test.

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:

  • Changed behavior and consequential failure paths have appropriate tests
  • Regression sensitivity is demonstrated; timing of the test is reported honestly
  • Each test failed for expected reason (feature missing, not typo)
  • Wrote minimal code to pass each test
  • All tests pass
  • Output pristine (no errors, warnings)
  • Tests use real code (mocks only if unavoidable)
  • Edge cases and errors covered

Record any unmet check and its consequence. Do not erase work or claim an unobserved failure to complete a checklist.

When Stuck

ProblemSolution
Don't know how to testWrite wished-for API. Write assertion first. Ask your human partner.
Test too complicatedDesign too complicated. Simplify interface.
Must mock everythingCode too coupled. Use dependency injection.
Test setup hugeExtract helpers. Still complex? Simplify design.

Debugging Integration

Bug found? Write failing test reproducing it. Follow TDD cycle. Test proves fix and prevents regression.

Prefer a reproducible regression for a bug fix; use another explicit verifier when a test cannot reasonably exercise the failure.

Testing Anti-Patterns

When adding mocks or test utilities, read @testing-anti-patterns.md to avoid common pitfalls:

  • Testing mock behavior instead of real behavior
  • Adding test-only methods to production classes
  • Mocking without understanding dependencies

Inputs and expected result

You need the user-visible requirement, the current implementation, a known runner and a controlled fixture. In the empty-email example, the failure must be “missing validation”, not a network outage. Expected: the regression fails on the defective behavior and passes after the smallest repair, while existing valid submissions still work.

Limitations

  • A passing unit test does not prove browser, packaged-runtime or provider integration behavior.
  • Retry examples assume retry-safe operations; production retries need explicit idempotency, cancellation and retryable-error policy.
  • Test-first order does not prevent incorrect requirements or over-mocking. Inspect assertions and real boundaries.
  • Preserve unrelated changes and use the project’s existing test commands rather than assuming every npm test accepts the same arguments.