test-driven-development

verified

254e31ad-2040-4dd2-bf41-0b501e19e6f1

Use when writing new code — the RED-GREEN-REFACTOR loop: write a failing test first, minimal code to pass, then refactor. Never write production code without a failing test.

Metadata

Skill ID
254e31ad-2040-4dd2-bf41-0b501e19e6f1
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
testingtddmethodologyquality
Signature
verified
Integrity
OK
Content hash
285e9de85e6d7f5dad757b28db4b257d1b643df0cda5a5e38049989ae310f5c4
Created
2026-08-15T05:29:32Z

Skill file

Raw skill file (markdown source)
# Test-Driven Development

**Use when** writing new code — including features, fixes, and refactors. The discipline: never write production code without a failing test. The loop is RED → GREEN → REFACTOR.

## The RED-GREEN-REFACTOR Loop

```text
 RED:   Write a test that fails (and fails for the RIGHT reason)
GREEN:  Write the MINIMAL code to make it pass (even if ugly)
REFACTOR: Clean up the code while tests stay green
```

### Step-by-Step with a Worked Example

**Feature**: A function `discount(price, customer_tier)` that applies:
- "premium": 20% off
- "standard": 10% off
- "none": 0% off

#### RED — Write the failing test

```python
# tests/test_pricing.py
import pytest
from app.pricing import discount

def test_discount_premium():
    assert discount(100.0, "premium") == 80.0

def test_discount_standard():
    assert discount(100.0, "standard") == 90.0

def test_discount_none():
    assert discount(100.0, "none") == 100.0

def test_discount_negative_price_raises():
    with pytest.raises(ValueError):
        discount(-10.0, "premium")
```

Run: `pytest tests/test_pricing.py -v` — **4 FAILED** (function doesn't exist yet)

#### GREEN — Minimal code to pass

```python
# app/pricing.py
def discount(price: float, tier: str) -> float:
    """Apply discount based on customer tier."""
    if price < 0:
        raise ValueError("price must be non-negative")
    rates = {"premium": 0.8, "standard": 0.9, "none": 1.0}
    if tier not in rates:
        raise ValueError(f"unknown tier: {tier}")
    return round(price * rates[tier], 2)
```

Run: `pytest tests/test_pricing.py -v` — **4 PASSED**

#### REFACTOR — Clean up while green

```python
# app/pricing.py — refactored
from enum import Enum

class Tier(Enum):
    PREMIUM = "premium"
    STANDARD = "standard"
    NONE = "none"

DISCOUNT_RATES = {
    Tier.PREMIUM: 0.8,
    Tier.STANDARD: 0.9,
    Tier.NONE: 1.0,
}

def discount(price: float, tier: str) -> float:
    if price < 0:
        raise ValueError("price must be non-negative")
    try:
        t = Tier(tier)
    except ValueError:
        raise ValueError(f"unknown tier: {tier!r}")
    return round(price * DISCOUNT_RATES[t], 2)
```

Run: `pytest tests/test_pricing.py -v` — **Still 4 PASSED**. Refactor didn't break anything.

## What to Test First

Test the *behavior contract*, not the implementation:

| Test the... | Example | Don't test the... |
|---|---|---|
| Input → Output | `discount(100, "premium") == 80` | Internal variable `rates` dict |
| Error cases | `discount(-10, "premium")` raises | Which line threw the exception |
| Edge cases | `discount(0, "premium") == 0` | Helper function calls |
| Invariants | `discount(x, tier) <= x` for all x,tier | Private method names |

## Discipline Rules

1. **Never write production code without a failing test.** If there's no test, there's no bug/feature.
2. **Only write enough code to make the test pass.** No future-proofing, no "might need this later."
3. **Refactor ONLY when tests are green.** Clean up duplications, rename, extract — but keep green.
4. **Every failure gets a test.** If CI breaks, first write a test that reproduces it.

## TDD Anti-Patterns

```python
# ANTI-PATTERN 1: Test passes without real implementation
def test_add():
    assert add(2, 3) == 5

def add(a, b):
    return 5  # "passes" but obviously wrong

# Fix: Add a second assertion that forces a real implementation
def test_add():
    assert add(2, 3) == 5
    assert add(7, 1) == 8  # forces real logic

# ANTI-PATTERN 2: Writing implementation first, then bolting on tests
# (the tests pass because you wrote them to fit the code, not the spec)

# ANTI-PATTERN 3: Skipping RED entirely
# If the test passes before you write code, it's not testing anything new
```

## Guardrails

- The test MUST fail before you write code. If it passes immediately, your test is wrong.
- Don't write more production code than the test demands. YAGNI (You Ain't Gonna Need It).
- Don't refactor on RED. You'll lose the safety net.
- Each cycle should be minutes, not hours. If a cycle takes >30m, your test is too big.

## Pitfalls

| Pitfall | Fix |
|---------|-----|
| Test passes immediately (not RED) | The test isn't calling the right function or the behavior already exists |
| Writing implementation-first then testing | Delete the implementation, write the test, start over |
| Testing implementation details | Test behavior: "given X input, expect Y output/error" |
| Giant tests (50+ lines per test) | One assertion per test; split into multiple test functions |
| Not running the full suite after GREEN | `pytest tests/ -x` after every cycle |

## Verify / Checklist

- [ ] RED: Test fails for the right reason (function missing or wrong output)
- [ ] GREEN: Minimal code makes the test pass
- [ ] REFACTOR: Code cleaned, tests still green
- [ ] Full test suite passes after each cycle
- [ ] Test covers happy path + error case + at least one edge case
- [ ] No implementation details leaked into test assertions

Attached files

No attached files.