test-driven-development
Use when implementing any feature or bugfix before writing implementation code
When & Why to Use This Skill
This Claude skill facilitates a rigorous Test-Driven Development (TDD) workflow to enhance software reliability and architectural design. By enforcing the 'Red-Green-Refactor' cycle, it ensures that every line of production code is justified by a failing test, significantly reducing technical debt, preventing regressions, and promoting cleaner, more maintainable codebases.
Use Cases
- New Feature Development: Defining the expected behavior and interface through tests before writing any implementation code to ensure a consumer-first design.
- Bug Fixing: Creating a specific, failing test case that reproduces a reported issue to verify the fix and ensure the bug never returns.
- Safe Refactoring: Providing a robust safety net of tests that allows developers to optimize and clean up internal code structures without changing external behavior.
- API and Interface Design: Using a test-first approach to think through how a module or function will be used before committing to a specific implementation logic.
| name | test-driven-development |
|---|---|
| description | Use when implementing any feature or bugfix before writing implementation code |
Test-Driven Development
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.
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
When to Use
Always:
- New features
- Bug fixes
- Refactoring
- Behavior changes
Exceptions (ask your human partner):
- Throwaway prototypes
- Generated code
- Configuration files
Red-Green-Refactor Cycle
RED - Write Failing Test
Write ONE minimal test showing what should happen.
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);
});
Verify it fails correctly:
- Run the test
- Confirm it fails for the RIGHT reason
- If it passes → your test is wrong
GREEN - Minimal Code
Write the MINIMUM code to make the test pass.
- Don't optimize
- Don't handle edge cases yet
- Don't make it pretty
- Just make it pass
REFACTOR - Clean Up
Only after green:
- Improve code quality
- Remove duplication
- Improve naming
- Stay green (run tests after each change)
Test Quality Checklist
- Test name describes the behavior
- Tests ONE thing
- Fails for the right reason
- Fast (< 100ms typically)
- No test interdependencies
- Readable without comments
Common Mistakes
| Mistake | Fix |
|---|---|
| Test too big | Split into smaller tests |
| Testing implementation | Test behavior instead |
| Not watching it fail | Always run test before writing code |
| Skipping refactor | Technical debt accumulates |
| Multiple behaviors per test | One assertion focus per test |
The Discipline
Thinking "skip TDD just this once"? Stop. That's rationalization.
TDD is not about testing. It's about design. Tests-first forces you to think about the interface before the implementation.