test-driven-development · sickn33 — Claude Code skill: setup and use
Guides coding agents through a strict test-driven development workflow: red, green, and refactor. Useful when fixing bugs or implementing new features with regression checks.
npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a claude-codeWhat test-driven-development does
This skill enforces a disciplined Red-Green-Refactor cycle. The agent writes a single behavioral test before touching any production logic. It must run the test runner, watch the test fail, and verify the failure message points to the missing feature rather than an import error or typo.
Once red is confirmed, the agent writes the absolute minimum code required to turn the suite green. It forbids speculative abstractions and premature feature additions. If existing user code is already present, the skill prohibits resetting the branch or deleting work; instead, the agent adds characterization tests and reports timing honestly.
Manufacturing unit tests for copy adjustments, generated assets, or low-impact configurations is explicitly discouraged in favor of direct validation.
What it helps with
- reproduce bugs with failing regression tests before editing code
- implement features using the minimal code needed to pass tests
- run safe refactoring passes while keeping test suites green
- isolate external dependencies without overusing mocks
Example prompts for the agent
- Use TDD to fix the bug where an empty email is accepted by the form
- Write a failing test for retryOperation, verify the failure, and then write minimal code to pass
- Refactor the validation logic while keeping all existing tests green
How to install test-driven-development
1Skills CLI (Vercel)
npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a claude-code2Manually: put the files in place
~/.claude/skills/test-driven-development/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/test-driven-development/
1Skills CLI (Vercel)
npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a codex2Manually: put the files in place
.agents/skills/test-driven-development/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a cursor2Manually: put the files in place
.cursor/skills/test-driven-development/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install sickn33/agentic-awesome-skills test-driven-development2Manually: put the files in place
.github/skills/test-driven-development/SKILL.md1Skills CLI (Vercel)
npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a gemini-cli2Platform CLI
gemini skills install https://github.com/sickn33/agentic-awesome-skills --path skills/test-driven-developmentManually: put the files in place
.devin/skills/test-driven-development/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
From the same set80
The whole set →Other versions of test-driven-development
Similar
Questions about test-driven-development
How do I install test-driven-development in Claude Code?
Run npx skills add sickn33/agentic-awesome-skills --skill test-driven-development -a claude-code in your terminal.
What does the test-driven-development skill do?
It instructs the agent to follow a strict Red-Green-Refactor loop, requiring a verified failing test before any production code or bug fix is implemented.
What should the agent do if code was already written before the test?
The skill forbids resetting the branch or erasing existing work. The agent must keep the code, add characterization tests, and document honestly whether the test preceded the fix.
Source text
SKILL.md · MIT
---
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
```dot
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.**
```bash
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.**
```bash
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 |
## 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**
```typescript
test('rejects empty email', async () => {
const result = await submitForm({ email: '' });
expect(result.error).toBe('Email required');
});
```
**Verify RED**
```bash
$ npm test
FAIL: expected 'Email required', got undefined
```
**GREEN**
```typescript
function submitForm(data: FormData) {
if (!data.email?.trim()) {
return { error: 'Email required' };
}
// ...
}
```
**Verify GREEN**
```bash
$ 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
| Problem | Solution |
|---------|----------|
| Don't know how to test | Write wished-for API. Write assertion first. Ask your human partner. |
| Test too complicated | Design too complicated. Simplify interface. |
| Must mock everything | Code too coupled. Use dependency injection. |
| Test setup huge | Extract 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.
© sickn33 · MIT · sickn33/agentic-awesome-skills