Marketplace tdd
Test-Driven Development workflow - write tests first, then implementation
git clone https://github.com/aiskillstore/marketplace
T=$(mktemp -d) && git clone --depth=1 https://github.com/aiskillstore/marketplace "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/benny9193/tdd" ~/.claude/skills/aiskillstore-marketplace-tdd && rm -rf "$T"
skills/benny9193/tdd/SKILL.mdTest-Driven Development (TDD)
Follow the Red-Green-Refactor cycle strictly. Never write production code without a failing test first.
The TDD Cycle
┌─────────────────────────────────────────┐ │ │ │ ┌───────┐ │ │ │ RED │ Write a failing test │ │ └───┬───┘ │ │ │ │ │ ▼ │ │ ┌───────┐ │ │ │ GREEN │ Write minimal code to pass│ │ └───┬───┘ │ │ │ │ │ ▼ │ │ ┌──────────┐ │ │ │ REFACTOR │ Improve without │ │ └────┬─────┘ breaking tests │ │ │ │ │ └──────────────────────────────┘
Rules (Non-Negotiable)
-
No production code without a failing test
- Write the test first
- Watch it fail
- Only then write implementation
-
Write the minimum test to fail
- One assertion per test
- Test behavior, not implementation
- Start with the simplest case
-
Write the minimum code to pass
- Don't anticipate future needs
- Hardcode if that makes it pass
- Generalize only when forced by tests
-
Refactor only when green
- All tests must pass before refactoring
- Keep tests passing during refactoring
- Small steps only
Workflow
Phase 1: RED (Write Failing Test)
// Start with what you want the API to look like describe('Calculator', () => { it('should add two numbers', () => { const calc = new Calculator(); expect(calc.add(2, 3)).toBe(5); }); });
Run test → Watch it fail → Confirm it fails for the RIGHT reason
Phase 2: GREEN (Make It Pass)
// Write the SIMPLEST code that passes class Calculator { add(a: number, b: number): number { return 5; // Yes, hardcoding is fine here! } }
Run test → Watch it pass → Celebrate (briefly)
Phase 3: Add Another Test (Force Generalization)
it('should add different numbers', () => { const calc = new Calculator(); expect(calc.add(1, 1)).toBe(2); // Forces real implementation });
Phase 4: GREEN Again
class Calculator { add(a: number, b: number): number { return a + b; // Now we generalize } }
Phase 5: REFACTOR
With green tests, improve the code:
- Remove duplication
- Improve names
- Extract methods
- Keep tests green throughout
Test Naming Convention
Use this pattern:
should [expected behavior] when [condition]
it('should return empty array when input is null') it('should throw ValidationError when email is invalid') it('should retry 3 times when connection fails')
TDD Checklist
Before writing ANY code, verify:
- I have a failing test
- The test fails for the expected reason
- The test is testing behavior, not implementation
- The test name describes what it verifies
Before marking GREEN:
- Test passes
- I wrote the minimum code necessary
- I didn't add "extra" functionality
Before REFACTORING:
- All tests are green
- I have a specific improvement in mind
- Changes are small and incremental
Common TDD Mistakes
❌ Writing Tests After Code
This is not TDD. Tests written after tend to test implementation, not behavior.
❌ Writing Too Many Tests at Once
Write ONE test, make it pass, then write the next. Stay in the cycle.
❌ Making Big Jumps
If your implementation is more than a few lines, you skipped steps. Add intermediate tests.
❌ Testing Implementation Details
// BAD - tests implementation expect(user._hashedPassword).toMatch(/^[a-f0-9]{64}$/); // GOOD - tests behavior expect(user.verifyPassword('correct')).toBe(true);
❌ Refactoring While Red
Never refactor with failing tests. Get green first.
Starting a New Feature with TDD
- List behaviors the feature needs (user stories → test cases)
- Order from simplest to most complex
- Write first test for simplest behavior
- Cycle through Red-Green-Refactor
- Add tests for edge cases
- Add tests for error handling
Example Session
Goal: Implement a
slugify function
// Test 1: Simplest case it('should lowercase the input', () => { expect(slugify('Hello')).toBe('hello'); }); // Implementation: return input.toLowerCase(); // Test 2: Handle spaces it('should replace spaces with hyphens', () => { expect(slugify('Hello World')).toBe('hello-world'); }); // Implementation: return input.toLowerCase().replace(/ /g, '-'); // Test 3: Handle special characters it('should remove special characters', () => { expect(slugify('Hello, World!')).toBe('hello-world'); }); // Implementation: add .replace(/[^a-z0-9-]/g, ''); // Test 4: Edge case it('should handle empty string', () => { expect(slugify('')).toBe(''); }); // Already passes! No change needed.
Remember
"TDD is not about testing. It's about design."
The tests drive you toward better, more modular code. Trust the process.