Skip to content
Testing
Skill

/tdd-london-chicago

Apply London (mock-based) and Chicago (state-based) TDD schools. Use when practicing test-driven development or choosing testing style for your context.

From plugin
agentic-qe
436200 skills169 agents149 commands
Install
$ npx -y skills add proffesor-for-testing/agentic-qe --skill tdd-london-chicago --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/tdd-london-chicago

Context preview

The summary Claude sees to decide when to auto-load this skill.

Apply London (mock-based) and Chicago (state-based) TDD schools. Use when practicing test-driven development or choosing testing style for your context.

SKILL.md

tdd-london-chicago.SKILL.md
name: tdd-london-chicago
description: "Apply London (mock-based) and Chicago (state-based) TDD schools. Use when practicing test-driven development or choosing testing style for your context."
category: development-practices
priority: high
tokenEstimate: 1100
agents: [qe-test-generator, qe-test-implementer, qe-test-refactorer]
implementation_status: optimized
optimization_version: 1.0
last_optimized: 2025-12-02
dependencies: []
quick_reference_card: true
tags: [tdd, testing, london-school, chicago-school, red-green-refactor, mocks]
trust_tier: 2
validation:
  schema_path: schemas/output.json
  validator_path: scripts/validate-config.json

Test-Driven Development: London & Chicago Schools

<default_to_action> When implementing TDD or choosing testing style: 1. IDENTIFY code type: domain logic → Chicago, external deps → London 2. WRITE failing test first (Red phase) 3. IMPLEMENT minimal code to pass (Green phase) 4. REFACTOR while keeping tests green (Refactor phase) 5. REPEAT cycle for next functionality

**Quick Style Selection:**

  • Pure functions/calculations → Chicago (real objects, state verification)
  • Controllers/services with deps → London (mocks, interaction verification)
  • Value objects → Chicago (test final state)
  • API integrations → London (mock external services)
  • Mix both in practice (London for controllers, Chicago for domain)

**Critical Success Factors:**

  • Tests drive design, not just verify it
  • Make tests fail first to ensure they test something
  • Write minimal code - no features beyond what's tested

</default_to_action>

Quick Reference Card

When to Use

  • Starting new feature with test-first approach
  • Refactoring legacy code with test coverage
  • Teaching TDD practices to team
  • Choosing between mocking vs real objects

TDD Cycle

| Phase | Action | Discipline | |-------|--------|------------| | **Red** | Write failing test | Verify it fails, check message is clear | | **Green** | Minimal code to pass | No extra features, don't refactor | | **Refactor** | Improve structure | Keep tests passing, no new functionality |

School Comparison

| Aspect | Chicago (Classicist) | London (Mockist) | |--------|---------------------|------------------| | Collaborators | Real objects | Mocks/stubs | | Verification | State (assert outcomes) | Interaction (assert calls) | | Isolation | Lower (integrated) | Higher (unit only) | | Refactoring | Easier | Harder (mocks break) | | Design feedback | Emerges from use | Explicit from start |

Agent Coordination

  • `qe-test-generator`: Generate tests in both schools
  • `qe-test-implementer`: Implement minimal code (Green)
  • `qe-test-refactorer`: Safe refactoring (Refactor)

---

Chicago School (State-Based)

**Philosophy:** Test observable behavior through public API. Keep tests close to consumer usage.

// State verification - test final outcome
describe('Order', () => {
  it('calculates total with tax', () => {
    const order = new Order();
    order.addItem(new Product('Widget', 10.00), 2);
    order.addItem(new Product('Gadget', 15.00), 1);

    expect(order.totalWithTax(0.10)).toBe(38.50);
  });
});

**When Chicago Shines:**

  • Domain logic with clear state
  • Algorithms and calculations
  • Value objects (`Money`, `Email`)
  • Simple collaborations
  • Learning new domain

---

London School (Mock-Based)

**Philosophy:** Test each unit in isolation. Focus on how objects collaborate.

// Interaction verification - test method calls
describe('Order', () => {
  it('delegates tax calculation', () => {
    const taxCalculator = {
      calculateTax: jest.fn().mockReturnValue(3.50)
    };
    const order = new Order(taxCalculator);
    order.addItem({ price: 10 }, 2);

    order.totalWithTax();

    expect(taxCalculator.calculateTax).toHaveBeenCalledWith(20.00);
  });
});

**When London Shines:**

  • External integrations (DB, APIs)
  • Command patterns with side effects
  • Complex workflows
  • Slow operations (network, I/O)

---

Mixed Approach (Recommended)

// London for controller (external deps)
describe('OrderController', () => {
  it('creates order and sends confirmation', async () => {
    const orderService = { create: jest.fn().mockResolvedValue({ id: 123 }) };
    const emailService = { send: jest.fn() };

    const controller = new OrderController(orderService, emailService);
    await controller.placeOrder(orderData);

    expect(orderService.create).toHaveBeenCalledWith(orderData);
    expect(emailService.send).toHaveBeenCalled();
  });
});

// Chicago for domain logic
describe('OrderService', () => {
  it('applies discount when threshold met', () => {
    const service = new OrderService();
    const order = service.create({ items: [...], total: 150 });

    expect(order.discount).toBe(15); // 10% off > $100
  });
});

---

Common Pitfalls

❌ Over-Mocking (London)

// BAD - mocking everything
const product = { getName: jest.fn(), getPrice: jest.fn() };

**Better:** Only mock external dependencies.

❌ Mocking Internals

// BAD - testing private methods
expect(order._calculateSubtotal).toHaveBeenCalled();

**Better:** Test public behavior only.

❌ Test Pain = Design Pain

  • Need many mocks? → Too many dependencies
  • Hard to set up? → Constructor does too much
  • Can't test without database? → Coupling issue

---

Agent-Assisted TDD

// Agent generates tests in both schools
await Task("Generate Tests", {
  style: 'chicago',      // or 'london'
  target: 'src/domain/Order.ts',
  focus: 'state-verification'  // or 'collaboration-patterns'
}, "qe-test-generator");

// Agent-human ping-pong TDD
// Human writes test concept
const testIdea = "Order applies 10% discount when total > $100";

// Agent generates formal failing test (Red)
await Task("Create Failing Test", testIdea, "qe-test-generator");

// Human writes minimal code (Green)

// Agent suggests refactorings
await Task("Suggest Refactorings"
Read more
Ships withagentic-qe

AI-powered quality engineering agents that generate tests, find coverage gaps, detect flaky tests, and learn your codebase patterns — across 11 coding agent platforms.

Get the whole plugin

Other skills on agentic-qe.