Skip to main content

Lesson 6: TDD Workflow

Time: 45 minutes Goal: Master test-first development


Why TDD?​

TDD is Optional

TDD improves code quality but slows development. Use it when correctness matters (critical logic, APIs). Skip it for prototyping.

Test-After (Traditional)​

Write Code β†’ Write Tests β†’ Find Bugs β†’ Fix β†’ Repeat
└── Often: No tests at all

Test-First (TDD)​

Write Test β†’ Watch Fail β†’ Write Code β†’ Watch Pass β†’ Refactor
β–Ό β–Ό β–Ό β–Ό β–Ό
Define Verify Minimal Verify Improve
behavior test works implement it works quality

The Red-Green-Refactor Cycle​

         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ RED β”‚ Write failing test
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ GREEN β”‚ Minimal code to pass
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚REFACTOR β”‚ Clean up, keep green
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
β–Ό
[Next Test]

SpecWeave TDD Commands​

/specweave:tdd-cycle     # Full orchestration
/specweave:tdd-red # Write failing tests
/specweave:tdd-green # Implement to pass
/specweave:tdd-refactor # Clean up code

Example: Calculator​

Let's build a calculator using TDD.

Step 1: Create Increment​

/specweave:increment "Calculator with basic operations"

Step 2: Start TDD​

/specweave:tdd-cycle

Phase 1: RED (Write Failing Tests)​

// tests/calculator.test.ts
import { describe, it, expect } from 'vitest';
import { add } from '../src/calculator';

describe('add()', () => {
it('adds two positives', () => {
expect(add(2, 3)).toBe(5);
});

it('adds negatives', () => {
expect(add(-5, 3)).toBe(-2);
});

it('handles zero', () => {
expect(add(0, 0)).toBe(0);
});
});

Running tests:

FAIL tests/calculator.test.ts
βœ• adds two positives
βœ• adds negatives
βœ• handles zero

5 tests failed (expected!)

βœ… RED PHASE COMPLETE

Phase 2: GREEN (Make Tests Pass)​

// src/calculator.ts
export function add(a: number, b: number): number {
return a + b;
}

Running tests:

PASS tests/calculator.test.ts
βœ“ adds two positives
βœ“ adds negatives
βœ“ handles zero

5 tests passed!

βœ… GREEN PHASE COMPLETE

Phase 3: REFACTOR (Improve Code)​

// src/calculator.ts (after refactor)
export function add(a: number, b: number): number {
if (typeof a !== 'number' || typeof b !== 'number') {
throw new TypeError('Both arguments must be numbers');
}
if (Number.isNaN(a) || Number.isNaN(b)) {
throw new TypeError('Arguments cannot be NaN');
}
return a + b;
}

Verification:

PASS tests/calculator.test.ts
βœ“ All original tests passing
+ 2 new validation tests added

βœ… REFACTOR PHASE COMPLETE

BDD in tasks.md​

Every task includes BDD test plans:

### T-004: Implement divide()

**Test Plan** (BDD):

**Scenario 1: Normal division**
- **Given** two numbers 10 and 2
- **When** divide(10, 2) is called
- **Then** result is 5

**Scenario 2: Division by zero**
- **Given** divisor is 0
- **When** divide(10, 0) is called
- **Then** Error "Division by zero" is thrown

**Test Cases**:
- divide_normalDivision_returnsQuotient
- divide_byZero_throwsError

TDD Best Practices​

Test Behavior, Not Implementation​

// ❌ BAD: Testing implementation
it('uses _calculateInternal method', () => {
const spy = vi.spyOn(calc, '_calculateInternal');
calc.add(1, 2);
expect(spy).toHaveBeenCalled();
});

// βœ… GOOD: Testing behavior
it('adds two numbers', () => {
expect(calc.add(1, 2)).toBe(3);
});

Descriptive Test Names​

// ❌ BAD
it('test1', () => { ... });

// βœ… GOOD
it('add_twoPositives_returnsSum', () => { ... });

One Assertion Per Test​

// ❌ BAD: Multiple unrelated assertions
it('calculator works', () => {
expect(calc.add(1, 2)).toBe(3);
expect(calc.subtract(5, 3)).toBe(2);
});

// βœ… GOOD: Focused tests
it('adds numbers', () => {
expect(calc.add(1, 2)).toBe(3);
});

it('subtracts numbers', () => {
expect(calc.subtract(5, 3)).toBe(2);
});

Try It Yourself​

Build a string calculator using TDD:

Requirements:

  • "1,2" returns 3
  • Empty string returns 0
  • Single number returns that number
  • Negative numbers throw error
/specweave:increment "String calculator with TDD"
/specweave:tdd-cycle
/specweave:next

Glossary Terms Used​

  • TDD β€” Test-Driven Development
  • BDD β€” Behavior-Driven Development (Given/When/Then)
  • Unit Testing β€” Testing individual functions
  • Test Coverage β€” Percentage of code tested

Key Takeaways​

CommandPhasePurpose
/specweave:tdd-cycleAllFull orchestration
/specweave:tdd-redREDWrite failing tests
/specweave:tdd-greenGREENImplement to pass
/specweave:tdd-refactorREFACTORImprove code

The TDD mantra:

  1. Red: Write a failing test
  2. Green: Make it pass (minimal code)
  3. Refactor: Clean up (keep tests green)

What's Next?​

Connect SpecWeave to your project management tools.

:next β†’ Lesson 7: External Tools