# Testing Patterns

> TDD workflow and test strategy patterns including test pyramid, coverage strategies, mocking approaches, and anti-patterns. Load when writing tests, designing test strategies, or reviewing test coverage.

- Skill: `groupzer0/testing-patterns` (Agent Skill, multi-file: 6 files)
- Install (CLI): `npx skillmds@latest add groupzer0/testing-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/groupzer0/testing-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- License: MIT
- Author: groupzer0 (https://skillmd.com/u/groupzer0)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/groupzer0/testing-patterns

---


# Testing Patterns

Systematic approach to effective testing. Use this skill when:
- Writing or changing tests (load anti-patterns reference)
- Designing test strategies for new features
- Reviewing test coverage adequacy
- Implementing test frameworks or infrastructure

---

## Test-Driven Development (TDD)

**TDD is MANDATORY for new feature code.** Write tests before implementation.

### The TDD Cycle

```
┌─────────────────────────────────────────┐
│                                         │
│   1. RED     → Write failing test       │
│   2. GREEN   → Minimal code to pass     │
│   3. REFACTOR → Clean up, tests stay green │
│   4. REPEAT                             │
│                                         │
└─────────────────────────────────────────┘
```

### Why TDD?

| Benefit | How TDD Delivers |
|---------|------------------|
| **Prevents over-mocking** | You see what test needs before mocking |
| **No test-only production code** | Minimal implementation = no extras |
| **Tests real behavior** | Failing test proves it tests something real |
| **Better design** | Testable code = loosely coupled code |

### When TDD Applies

| Situation | TDD? | Notes |
|-----------|------|-------|
| New features | ✅ Always | Core workflow |
| Behavior changes | ✅ Always | Modify test first, then code |
| Bug fixes | ✅ Preferred | Write test reproducing bug first |
| Pure refactors | ⚠️ Optional | Existing tests should cover |
| Exploratory spikes | ❌ Skip | But TDD rewrite after |

### TDD Violations

**If implementation arrives without tests:**
1. Reject with "TDD Required"
2. Specify which tests should exist
3. Implementation writes tests first, then code

See [references/testing-anti-patterns.md](references/testing-anti-patterns.md) for detailed anti-patterns and gate functions.

## Test Pyramid

```
        /\
       /  \        E2E Tests (10%)
      /----\       Slow, expensive, few
     /      \
    /--------\     Integration Tests (20%)
   /          \    Medium speed, focused
  /------------\
 /              \  Unit Tests (70%)
/________________\ Fast, isolated, many
```

### Unit Tests
**What:** Test single function/class in isolation
**When:** All business logic, utilities, data transformations
**Speed:** Milliseconds
**Isolation:** Mock external dependencies (DB, network, filesystem)

```python
# Good unit test
def test_calculate_discount():
    order = Order(items=[Item(price=100)])
    assert order.calculate_discount(0.2) == 80

# Bad: tests integration, not unit
def test_order_discount():
    db.create_order(...)  # Touches database
    api.apply_coupon(...)  # External call
```

### Integration Tests
**What:** Test component interactions
**When:** Database queries, API contracts, service boundaries
**Speed:** Seconds
**Isolation:** Real dependencies for component under test

```python
# Integration: tests DB interaction
def test_user_repository_finds_by_email():
    repo = UserRepository(test_db)
    repo.create(User(email="test@example.com"))
    found = repo.find_by_email("test@example.com")
    assert found.email == "test@example.com"
```

### E2E Tests
**What:** Test full user workflows
**When:** Critical paths, smoke tests, happy paths
**Speed:** Minutes
**Isolation:** None—tests complete system

```python
# E2E: tests full flow
def test_user_can_checkout():
    browser.goto("/")
    browser.login("user@example.com", "password")
    browser.add_to_cart("product-1")
    browser.checkout()
    assert browser.has_text("Order confirmed")
```

---

## Coverage Strategy

### What to Cover (Priority Order)

1. **Business logic** — revenue-impacting calculations
2. **Security boundaries** — auth, validation, access control
3. **Error paths** — exception handling, edge cases
4. **Integration points** — API contracts, DB queries
5. **Happy paths** — standard user workflows

### What NOT to Prioritize

- Getters/setters without logic
- Framework code (already tested)
- Third-party libraries
- One-time scripts
- UI layout (unless critical)

### Coverage Targets

| Type | Target | Notes |
|------|--------|-------|
| Unit | 80%+ | Focus on logic, not coverage number |
| Integration | Critical paths | Don't test every permutation |
| E2E | Happy paths only | 5-10 core scenarios |

---

## Edge Case Generation

### Systematic Approach

**For numeric inputs:**
- Zero
- Negative numbers
- Very large numbers (overflow)
- Floating point precision
- Boundary values (n-1, n, n+1)

**For string inputs:**
- Empty string
- Very long string
- Unicode/emoji
- Special characters
- Whitespace only
- SQL/HTML injection attempts

**For collections:**
- Empty
- Single element
- Many elements
- Duplicates
- null/undefined elements

**For dates:**
- Leap years
- Timezone boundaries
- DST transitions
- Far past/future
- Invalid dates

### Example Matrix

```markdown
| Input | Scenario | Expected |
|-------|----------|----------|
| price | 0 | Free item handling |
| price | -5 | Validation error |
| price | 999999.99 | Large number display |
| name | "" | Required field error |
| name | "a"*1000 | Truncation or error |
| email | "test" | Invalid format error |
```

---

## Mocking Patterns

### When to Mock

| Context | Mock What? | Reason |
|---------|------------|--------|
| Unit tests | External dependencies (DB, network, time) | Isolation + speed |
| Integration tests | External services only | Test real component interaction |
| E2E tests | Nothing | Test real system |

> ⚠️ **TDD prevents over-mocking**: If you write the test first and watch it fail, you know exactly what needs mocking.

### Mock vs Stub vs Spy

```python
# Stub: Returns canned response
payment_gateway = Mock()
payment_gateway.charge.return_value = {"status": "success"}

# Mock: Verifies interactions
email_service = Mock()
order.complete()
email_service.send.assert_called_once_with(
    to="user@example.com",
    subject="Order Confirmed"
)

# Spy: Wraps real implementation
real_logger = Logger()
spy_logger = Mock(wraps=real_logger)
# Calls real method but records calls
```

### The Iron Laws of Mocking

1. **NEVER test mock behavior** — Use mocks to isolate your unit from dependencies, but assert on the unit's behavior, not the mock's existence. If your assertion is `expect(mockThing).toBeInTheDocument()`, you're testing the mock, not the code.
2. **NEVER mock without understanding** — Know side effects before isolating
3. **NEVER create incomplete mocks** — Mirror real API structure completely

### Anti-Pattern Red Flags

- Mock setup longer than test logic
- Assertions on `*-mock` test IDs
- Can't explain why mock is needed
- Mocking "just to be safe"

**Full anti-pattern details**: [references/testing-anti-patterns.md](references/testing-anti-patterns.md)

---

## Test Quality Checklist

| Quality | Check |
|---------|-------|
| **Readable** | Can a new dev understand in 30 seconds? |
| **Isolated** | Does it fail independently of other tests? |
| **Fast** | Unit tests < 100ms, Integration < 5s? |
| **Deterministic** | Same result every run? |
| **Focused** | One assertion per test (or logical group)? |
| **Maintainable** | Will this break for wrong reasons? |

---

## Test Naming

```
test_[unit]_[scenario]_[expected]

test_calculateDiscount_withExpiredCoupon_returnsZero
test_userRepository_findByEmail_whenNotFound_returnsNone
test_checkout_withEmptyCart_showsError
```

See [references/testing-frameworks.md](references/testing-frameworks.md) for framework-specific guidance.

