# TDD Workflow Skill

> **Test-Driven Development: Red-Green-Refactor methodology.**

---

## The TDD Cycle

```
┌─────────────────────────────────────────┐
│            RED → GREEN → REFACTOR       │
│                                         │
│   1. Write failing test (RED)           │
│   2. Write minimal code to pass (GREEN) │
│   3. Improve code, keep tests (REFACTOR)│
│   4. Repeat                             │
└─────────────────────────────────────────┘
```

---

## Phase 1: RED (Write Failing Test)

### Before ANY Implementation

```python
# tests/test_calculator.py
def test_add_returns_sum_of_two_numbers():
    calc = Calculator()
    result = calc.add(2, 3)
    assert result == 5
```

### Run and Verify Failure

```bash
$ pytest tests/test_calculator.py -v
FAILED - NameError: Calculator is not defined
```

**This failure is essential.** It proves:
- The test actually tests something
- The feature doesn't accidentally exist
- You know what to implement

---

## Phase 2: GREEN (Minimal Implementation)

### Write Just Enough Code

```python
# src/calculator.py
class Calculator:
    def add(self, a: int, b: int) -> int:
        return a + b
```

### Verify Pass

```bash
$ pytest tests/test_calculator.py -v
PASSED
```

**Key principle:** Don't write more than needed. No extra features, no edge cases yet.

---

## Phase 3: REFACTOR (Improve)

### Clean Up While Tests Pass

```python
# Maybe extract, rename, or restructure
# But tests must stay green!
```

---

## Coverage Target

### Aim for >80%

```bash
# Python
pytest --cov=src --cov-report=term-missing tests/

# TypeScript
jest --coverage
```

### Coverage Report

```
Name                    Stmts   Miss  Cover   Missing
-----------------------------------------------------
src/calculator.py          10      0   100%
src/validator.py           25      3    88%   42, 55, 67
-----------------------------------------------------
TOTAL                      35      3    91%
```

---

## Test Patterns

### Python (pytest)

```python
# Parametrized tests
@pytest.mark.parametrize("a,b,expected", [
    (2, 3, 5),
    (0, 0, 0),
    (-1, 1, 0),
])
def test_add(a, b, expected):
    assert Calculator().add(a, b) == expected

# Fixtures
@pytest.fixture
def calculator():
    return Calculator()

def test_with_fixture(calculator):
    assert calculator.add(1, 1) == 2

# Mocking
def test_service_calls_api(mocker):
    mock_api = mocker.patch("src.service.api")
    mock_api.get.return_value = {"data": "value"}

    result = Service().fetch()

    mock_api.get.assert_called_once()
```

### TypeScript (Jest/Vitest)

```typescript
describe('Calculator', () => {
  describe('add', () => {
    it.each([
      [2, 3, 5],
      [0, 0, 0],
      [-1, 1, 0],
    ])('add(%i, %i) = %i', (a, b, expected) => {
      expect(new Calculator().add(a, b)).toBe(expected);
    });
  });
});
```

---

## Bug Fix TDD

### Write Test That Reproduces Bug First

```python
def test_bug_123_handles_special_characters():
    """Regression test for issue #123.

    Users couldn't login with special chars in password.
    """
    # This MUST fail before the fix
    auth = AuthService()
    result = auth.login("user@test.com", "p@ss!word#123")
    assert result.success
```

---

## Anti-Patterns

| Anti-Pattern | Problem | Solution |
|--------------|---------|----------|
| Test After Code | Tests shaped by implementation | Write test first |
| Test Implementation | Brittle, breaks on refactor | Test behavior |
| Too Many Mocks | Tests don't reflect reality | Use integration tests |
| Ignoring Failures | Hidden bugs | Fix immediately |
| Skipping Refactor | Messy code accumulates | Always refactor |

---

## Related Skills

- [coding-standards](../coding-standards/) - Code quality
- [verification-loop](../verification-loop/) - Pre-commit checks
