agentleFS
quatico-solutionsquatico-solutions/agent-skills/skills/test-driven-development/SKILL.md

test-driven-development

Skill2 starsChanged 4 months agoMITView on GitHub

Use when implementing any feature or bugfix, before writing implementation code. Triggers: TDD, test-driven, red-green-refactor, write test first, unit test workflow.

190 other projects use a version of this, 127 of them edited.

Adopted by

Most recently taken up by

Used with

Projects with it also use chinese-code-review, chinese-commit-conventions and 10 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • Leodorareluctant259Skill

    chinese-code-review

    中文代码审查规范——在保持专业严谨的同时,用符合国内团队文化的方式给出有效反馈

    Also in 8 projects that use this

  • Leodorareluctant259Skill

    chinese-commit-conventions

    中文 Git 提交规范 — 适配国内团队的 commit message 规范和 changelog 自动化

    Also in 8 projects that use this

  • obraSkill

    diagnosing-superpowers

    Use when a superpowers session went wrong and your human partner wants to know why — repeated work, ignored plans, stumbles, poor results, a skill that didn't fire, "it took too long", "why is it so expensive", "what is it doing" — or wants to build a bug report for the superpowers maintainers, for the current session or a past one identified by id or path, on any harness.

    Also in 19 projects that use this

  • LLl0k0laDSkill

    using-agent-skills

    Use when starting a session or when you need to discover which skill applies to the current task. This is the meta-skill that governs how all other skills are discovered and invoked.

    Also in 39 projects that use this

  • LLl0k0laDSkill

    git-workflow-and-versioning

    Use when making any code change. Use when committing, branching, resolving conflicts, or when you need to organize work across multiple parallel streams.

    Also in 47 projects that use this

  • MrCipherSmithSkill

    constraint-driven-development

    Use when a project needs a persistent, measurable quality bar — starting a project without documented standards, 'set up constraints', or when coverage/perf/a11y thresholds get re-debated per-PR. Produces CONSTRAINTS.md with enforceable numbers, ratchets, and a guard against silent weakening. NOT for throwaway prototypes or a one-off CI setup.

    Also in 15 projects that use this

  • +3 more from Leodorareluctant259
  • +3 more from LLl0k0laD

The file

Show its rules and text

We split this skill into the rules it gives an agent

17 rules. 9 are common, 8 are distinctive, and 0 are very specific to this project.

  • 9

    Common

    Most of the 178 edited versions we read keep these.

  • 8

    Distinctive

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Common:

    Write the test first. Watch it fail.

    line 15
  2. Common:

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

    line 17
  3. Distinctive:

    Follow the steps as written.

    line 19
  4. Common:

    Write code before the test?

    line 42
  5. Common:

    Write one minimal test showing what should happen.

    line 102
  6. Distinctive:

    Run it and confirm it fails, every time — that is the only evidence the test checks the right thing.

    line 149
  7. Common:

    Test fails (not errors)

    line 156
  8. Common:

    Write simplest code to pass the test.

    line 166
  9. Common:

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

    line 200
  10. Distinctive:

    Run it and confirm it passes, every time.

    line 204
  11. Common:

    Keep tests green. Don't add behavior.

    line 226
  12. Distinctive:

    Stop means the cycle ends, not the work: When an outer loop is driving — a BDD acceptance test, a plan step, a list of requested behaviours …

    line 258
  13. Common:

    Before marking work complete:

    line 297
  14. Distinctive:

    When working on user stories with acceptance criteria, TDD is the inner loop:

    line 326
  15. Distinctive:

    Outer loop (BDD): Write failing acceptance test in Gherkin

    line 328
  16. Distinctive:

    When adding mocks or test utilities, read testing-anti-patterns.md:

    line 336
  17. Distinctive:

    No exceptions without your human partner's permission.

    line 355

“Common” is measured: kept by at least 6 in 10 of the 178 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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.

Follow the steps as written; the exceptions below are the only ones.

When to Use

Always:

  • New features
  • Bug fixes
  • Behavior changes

Exceptions (ask your human partner):

  • Throwaway prototypes
  • Generated code
  • Configuration files
  • Pure refactoring (no behavior change)

Anything not on this list follows TDD. To skip it anyway, ask your human partner first.

The Iron Law

NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST

Write code before the test? Delete it. Start over. Implement fresh from tests.

Red-Green-Refactor

The TDD cycle with decision points:

digraph tdd_flow {
    rankdir=TB;
    compound=true;

    // Entry point
    run_all [label="Run All\nUnit Tests", shape=box, style="filled,rounded", fillcolor="#e8f5e9"];
    run_all_decision [label="", shape=diamond, width=0.4, height=0.4];

    // RED zone
    subgraph cluster_red {
        label="Red";
        style=filled;
        fillcolor="#ffcdd2";
        write_test [label="Write failing\nUnit Test", shape=box, style="filled,rounded", fillcolor="#ffebee"];
    }

    // GREEN zone
    subgraph cluster_green {
        label="Green";
        style=filled;
        fillcolor="#c8e6c9";
        change_behavior [label="Change\nBehavior", shape=box, style="filled,rounded", fillcolor="#e8f5e9"];
    }

    // REFACTOR zone
    subgraph cluster_refactor {
        label="Refactor";
        style=filled;
        fillcolor="#e0e0e0";
        change_structure [label="Change\nStructure", shape=box, style="filled,rounded", fillcolor="#fff9c4"];
    }

    run_unit [label="Run\nUnit Test", shape=box, style="filled,rounded", fillcolor="#fff9c4"];
    run_unit_decision [label="", shape=diamond, width=0.4, height=0.4];
    revert [label="Revert\n(test wrong!)", shape=box, style="filled,rounded", fillcolor="#ffccbc"];

    run_all -> run_all_decision;
    run_all_decision -> change_structure [label="Pass"];
    run_all_decision -> write_test [label="Fail"];
    write_test -> run_unit;
    run_unit -> run_unit_decision;
    run_unit_decision -> revert [label="Pass*\n(1st run)"];
    run_unit_decision -> change_behavior [label="Fail"];
    change_behavior -> run_unit;
    run_unit_decision -> change_structure [label="Pass"];
    change_structure -> run_all;
}

Key insight: A test that passes on the first run (before writing implementation) indicates the test is wrong—either it doesn't test new behavior, or the behavior already exists. Remove or rewrite such tests.

RED - Write Failing Test

Write one minimal test showing what should happen.

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

// Act const actual = await testObj.execute(operation);

// Assert expect(actual).toBe('success'); expect(attempts).toBe(3); });

Clear name, AAA pattern, 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 per test
  • Clear name describing behavior
  • Real code (mocks only when unavoidable)
  • AAA pattern (Arrange-Act-Assert)

Verify RED - Watch It Fail

Run it and confirm it fails, every time — that is the only evidence the test checks the right thing.

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 or delete test.

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

GREEN - Minimal Code

Write simplest code to pass the test.

<Good> ```typescript async function execute<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 execute<T>( fn: () => Promise<T>, options?: { maxRetries?: number; backoff?: 'linear' | 'exponential'; onRetry?: (attempt: number) => void; } ): Promise<T> { // YAGNI - no test requires this } ``` Over-engineered </Bad>

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

Verify GREEN - Watch It Pass

Run it and confirm it passes, every time.

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.

Stopping Rule

Red -> green -> refactor -> stop. One cycle ends when the test passes, the suite is green, and the output is clean. Then either start the next failing test or report done.

The cycle does not include:

  • Building test scaffolding, fixtures, harnesses or helpers that the current failing test does not require
  • Re-running a suite that already passed to confirm it still passes
  • Adding tests for behaviour no one asked for, found by inspecting existing code
  • Refactoring tests that are green and readable

Verification budget: each cycle should need one red run and one green run. If you find yourself running the same test a third time, stop and say why — the code, the test, or your understanding is wrong. Fix that rather than adding verification around it. If a cycle has produced more test-support code than production code, stop and say so.

Build scaffolding only when a test needs it, and only as much as that test needs. Infrastructure written before there is a failing test that uses it is speculative: delete it and let the next test pull it back in.

When budget is tight, ship the passing slice. Partial work that runs and is reported honestly beats a complete verification apparatus around nothing. Do not go quiet: if the cycle cannot close, report what passes, what fails, and what is untested.

Stop means the cycle ends, not the work. When an outer loop is driving — a BDD acceptance test, a plan step, a list of requested behaviours — stopping hands control back to it, and it decides whether another cycle is needed. Stopping is not the same as reporting done, and this rule is never a reason to leave requested work unfinished.

Naming, AAA and One Assertion

Variable naming (testObj, target, mock*, actual, expected), the Arrange-Act-Assert layout and one-behaviour-per-test live in the jest-testing-conventions skill. This skill owns the cycle; that one owns the mechanics.

Good Tests (FIRST)

QualityDescriptionBad Example
FastMilliseconds, not secondsTests requiring network calls
IsolatedNo shared state between testsTests depending on execution order
RepeatableSame result every runTests depending on current time
Self-validatingPass/fail, no manual inspectionTests requiring log analysis
TimelyWritten before/with codeTests added "later"

Mocks Are Necessary Evil

Prefer real code. Mocks verify collaboration, not behavior.

Mock only at boundaries:

  • External services (APIs, databases)
  • Non-deterministic operations (time, random)
  • Slow operations (network, file I/O)

Never mock:

  • The code under test
  • Simple collaborators that are fast and deterministic

See jest-testing-conventions for mocking patterns.

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 pristine (no errors, warnings)
  • Tests use real code (mocks only if unavoidable)
  • Edge cases and errors covered
  • AAA pattern in all tests
  • One behavior per test

A box you can't check is a gap to report or close, not a reason to restart.

Checked all boxes? Stop. Do not add an eleventh check of your own.

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.
Test passes first runTest is wrong—delete and rewrite.

Integration with Double Loop

When working on user stories with acceptance criteria, TDD is the inner loop:

  1. Outer loop (BDD): Write failing acceptance test in Gherkin
  2. Inner loop (TDD): Red-Green-Refactor for each unit needed
  3. Repeat TDD cycles until BDD test passes

See the outer BDD loop (outside-in development) for the complete workflow.

Testing Anti-Patterns

When adding mocks or test utilities, read testing-anti-patterns.md:

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

Related Skills

SkillUse For
jest-testing-conventionsJest-specific patterns (jest.fn/spyOn/mock, fake timers)
show-your-workLong-running or multi-cycle work — produce an artefact, not a claim

Final Rule

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

No exceptions without your human partner's permission.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.