| name | TestDrivenDevelopment |
| description | Disciplined TDD workflow enforcing red-green-refactor cycle and the "iron law" of no production code without failing tests first. USE WHEN user wants to write tests first OR implement new feature with TDD OR fix bugs with test coverage OR explicitly requests TDD approach. Enforces systematic test-first development with verification at each step. |
TestDrivenDevelopment
Strict test-driven development workflow based on the red-green-refactor cycle.
The Iron Law
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST
This is non-negotiable. Even when tempted to "just quickly fix" something, you MUST:
- Write a failing test that demonstrates the need
- Verify the test actually fails
- Write minimal code to make it pass
- Verify the test passes
- Refactor if needed
The RED-GREEN-REFACTOR Cycle
RED: Write Failing Test
- Write test that describes desired behavior
- Test MUST fail initially (if it passes, something's wrong)
- Failure message should be clear and specific
Example (Go):
func TestCalculateDiscount_TenPercent(t *testing.T) {
order := Order{Subtotal: 100.0, DiscountRate: 0.10}
got := order.CalculateTotal()
want := 90.0
if got != want {
t.Errorf("CalculateTotal() = %v, want %v", got, want)
}
}
Verify RED
- Run test suite
- Confirm new test fails
- Confirm failure reason matches expectation
- STOP if test doesn't fail - investigate why
go test ./...
# Expected: FAIL - undefined: Order.CalculateTotal
GREEN: Minimal Implementation
- Write ONLY enough code to make test pass
- Don't over-engineer
- Don't add features not tested
- Prioritize working over elegant
Example:
func (o Order) CalculateTotal() float64 {
return o.Subtotal * (1 - o.DiscountRate)
}
Verify GREEN
- Run full test suite
- Confirm ALL tests pass
- STOP if any test fails - fix before refactoring
go test ./...
# Expected: PASS
REFACTOR
- Improve code structure
- Eliminate duplication
- Enhance readability
- Run tests after each change
Example refactor:
func (o Order) CalculateTotal() float64 {
return o.ApplyDiscount(o.Subtotal)
}
func (o Order) ApplyDiscount(amount float64) float64 {
return amount * (1 - o.DiscountRate)
}
Good vs Bad Tests
Good Test (Tests Behavior)
func TestCalculateTotal_WithDiscount(t *testing.T) {
order := Order{Items: []Item{{Price: 100}}, Discount: 0.1}
got := order.CalculateTotal()
want := 90.0
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}
Bad Test (Tests Implementation)
func TestCalculateTotal_CallsDiscountFunc(t *testing.T) {
// Testing HOW instead of WHAT
// Brittle - breaks with refactoring
// Don't do this
}
Good Test Characteristics
- Tests behavior, not implementation
- Clear, descriptive name
- Single responsibility
- Independent (no shared state)
- Fast execution
- Deterministic
Red Flags
STOP immediately if you catch yourself:
- ❌ Writing production code before a failing test
- ❌ Modifying production code while tests are failing
- ❌ Skipping the "verify RED" step
- ❌ Writing overly complex implementation for green phase
- ❌ Refactoring with failing tests
- ❌ Adding features not covered by tests
When you notice these, return to RED phase.
TDD Workflow Checklist
For each feature/fix:
- Write failing test (RED)
- Run test, verify it fails with expected error
- Write minimal code to pass (GREEN)
- Run all tests, verify they pass
- Refactor if needed
- Run all tests again
- Commit with test and implementation together
Language-Specific Examples
Go
// RED: Test first
func TestParseConfig_ValidYAML(t *testing.T) {
yaml := "port: 8080\nhost: localhost"
config, err := ParseConfig([]byte(yaml))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if config.Port != 8080 {
t.Errorf("Port = %d, want 8080", config.Port)
}
}
// GREEN: Minimal implementation
func ParseConfig(data []byte) (*Config, error) {
var cfg Config
err := yaml.Unmarshal(data, &cfg)
return &cfg, err
}
Python
# RED: Test first
def test_calculate_discount_ten_percent():
order = Order(subtotal=100.0, discount_rate=0.10)
assert order.calculate_total() == 90.0
# GREEN: Minimal implementation
class Order:
def __init__(self, subtotal, discount_rate):
self.subtotal = subtotal
self.discount_rate = discount_rate
def calculate_total(self):
return self.subtotal * (1 - self.discount_rate)
Rust
// RED: Test first
#[test]
fn test_calculate_total_with_discount() {
let order = Order {
subtotal: 100.0,
discount_rate: 0.10,
};
assert_eq!(order.calculate_total(), 90.0);
}
// GREEN: Minimal implementation
impl Order {
fn calculate_total(&self) -> f64 {
self.subtotal * (1.0 - self.discount_rate)
}
}
When NOT to Use TDD
TDD isn't always the right choice:
- Exploratory coding: When you're not sure what you're building yet (use Brainstorming skill first)
- UI prototyping: Visual design often needs iteration before solidifying behavior
- Performance optimization: Sometimes you need to measure first, then optimize
- Glue code: Simple wiring between components may not need tests
For these cases: Build first, then add tests for the stable parts.
Integration with Other Skills
- Before TDD: Use Brainstorming skill to clarify requirements
- During TDD: Use SystematicDebugging if tests reveal unexpected behavior
- After TDD: Use code review workflows to validate test coverage
Examples
Example 1: Adding new feature with TDD
User: "Add a discount calculation feature"
→ Invoke TestDrivenDevelopment skill
→ RED: Write test for CalculateDiscount expecting 10% off
→ Verify: Run test, fails with "undefined: CalculateDiscount"
→ GREEN: Implement minimal CalculateDiscount function
→ Verify: Run test, passes
→ REFACTOR: Extract discount logic to separate method
→ Verify: Run all tests, still pass
→ Feature complete with test coverage
Example 2: Fixing a bug with TDD
User: "Fix the bug where negative prices crash the app"
→ Invoke TestDrivenDevelopment skill
→ RED: Write test with negative price expecting error or zero
→ Verify: Run test, crashes (reproduces bug)
→ GREEN: Add validation to reject negative prices
→ Verify: Run test, passes
→ REFACTOR: Improve error message
→ Bug fixed with regression test preventing recurrence
Example 3: User explicitly requests TDD
User: "Let's use TDD to build the authentication system"
→ Invoke TestDrivenDevelopment skill
→ Guide through RED-GREEN-REFACTOR for each auth feature:
- User registration
- Login validation
- Token generation
- Token verification
→ Each feature gets test first, then implementation
→ Comprehensive test coverage from the start
→ Confidence in authentication security
Example 4: Complex algorithm development
User: "Implement a binary search algorithm"
→ Invoke TestDrivenDevelopment skill
→ RED: Test with sorted array, search for existing element
→ GREEN: Implement basic binary search
→ RED: Test with element not in array
→ GREEN: Handle not found case
→ RED: Test with empty array
→ GREEN: Handle edge case
→ REFACTOR: Simplify logic
→ Algorithm complete with edge case coverage