tdd-london-chicago

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

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>
<default_to_action> 在实施TDD或选择测试风格时:
  1. 识别代码类型:领域逻辑 → 芝加哥流派,外部依赖 → 伦敦流派
  2. 先编写失败的测试(红阶段)
  3. 实现最小化代码使测试通过(绿阶段)
  4. 在保持测试通过的前提下进行重构(重构阶段)
  5. 为下一个功能重复该循环
快速风格选择:
  • 纯函数/计算逻辑 → 芝加哥流派(使用真实对象,状态验证)
  • 带有依赖的控制器/服务 → 伦敦流派(使用Mock,交互验证)
  • 值对象 → 芝加哥流派(测试最终状态)
  • API集成 → 伦敦流派(Mock外部服务)
  • 实践中混合使用(控制器用伦敦流派,领域逻辑用芝加哥流派)
关键成功要素:
  • 测试驱动设计,而非仅验证设计
  • 先让测试失败,确保测试能检测到问题
  • 编写最小化代码——不实现测试未覆盖的功能 </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实践时
  • 在Mock与真实对象之间做选择时

TDD Cycle

TDD循环

PhaseActionDiscipline
RedWrite failing testVerify it fails, check message is clear
GreenMinimal code to passNo extra features, don't refactor
RefactorImprove structureKeep tests passing, no new functionality
阶段操作准则
红阶段编写失败的测试验证测试确实失败,确保错误信息清晰
绿阶段编写最小化代码使测试通过不添加额外功能,不进行重构
重构阶段优化代码结构保持测试通过,不添加新功能

School Comparison

流派对比

AspectChicago (Classicist)London (Mockist)
CollaboratorsReal objectsMocks/stubs
VerificationState (assert outcomes)Interaction (assert calls)
IsolationLower (integrated)Higher (unit only)
RefactoringEasierHarder (mocks break)
Design feedbackEmerges from useExplicit from start
维度芝加哥流派(经典派)伦敦流派(Mock派)
协作对象真实对象Mocks/Stubs
验证方式状态验证(断言结果)交互验证(断言调用)
隔离程度较低(集成式)较高(仅单元)
重构难度较容易较难(Mock易失效)
设计反馈从使用中自然浮现从一开始就明确体现

Agent Coordination

Agent协作

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

  • qe-test-generator
    :生成两种流派的测试用例
  • qe-test-implementer
    :实现最小化代码(绿阶段)
  • qe-test-refactorer
    :安全重构(重构阶段)

Chicago School (State-Based)

芝加哥流派(基于状态)

Philosophy: Test observable behavior through public API. Keep tests close to consumer usage.
javascript
// 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

理念: 通过公共API测试可观察行为,让测试贴近消费者的使用方式。
javascript
// 状态验证 - 测试最终结果
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);
  });
});
芝加哥流派适用场景:
  • 具有清晰状态的领域逻辑
  • 算法与计算逻辑
  • 值对象(
    Money
    Email
  • 简单协作场景
  • 学习新领域时

London School (Mock-Based)

伦敦流派(基于Mock)

Philosophy: Test each unit in isolation. Focus on how objects collaborate.
javascript
// 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)

理念: 独立测试每个单元,聚焦对象间的协作方式。
javascript
// 交互验证 - 测试方法调用
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);
  });
});
伦敦流派适用场景:
  • 外部集成(数据库、API)
  • 带有副作用的命令模式
  • 复杂工作流
  • 慢速操作(网络、I/O)

Mixed Approach (Recommended)

混合方法(推荐)

javascript
// 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
  });
});

javascript
// 控制器使用伦敦流派(处理外部依赖)
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();
  });
});

// 领域逻辑使用芝加哥流派
describe('OrderService', () => {
  it('applies discount when threshold met', () => {
    const service = new OrderService();
    const order = service.create({ items: [...], total: 150 });

    expect(order.discount).toBe(15); // 满100美元减10%
  });
});

Common Pitfalls

常见陷阱

❌ Over-Mocking (London)

❌ 过度Mock(伦敦流派)

javascript
// BAD - mocking everything
const product = { getName: jest.fn(), getPrice: jest.fn() };
Better: Only mock external dependencies.
javascript
// 错误示例 - 对所有对象进行Mock
const product = { getName: jest.fn(), getPrice: jest.fn() };
优化方案: 仅对外部依赖进行Mock。

❌ Mocking Internals

❌ Mock内部实现

javascript
// BAD - testing private methods
expect(order._calculateSubtotal).toHaveBeenCalled();
Better: Test public behavior only.
javascript
// 错误示例 - 测试私有方法
expect(order._calculateSubtotal).toHaveBeenCalled();
优化方案: 仅测试公共行为。

❌ 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

  • 需要大量Mock?→ 依赖过多
  • 测试难以设置?→ 构造函数职责过重
  • 无法脱离数据库测试?→ 耦合问题

Agent-Assisted TDD

Agent辅助TDD

typescript
// 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", { preserveTests: true }, "qe-test-refactorer");

typescript
// Agent生成两种流派的测试用例
await Task("Generate Tests", {
  style: 'chicago',      // 或 'london'
  target: 'src/domain/Order.ts',
  focus: 'state-verification'  // 或 'collaboration-patterns'
}, "qe-test-generator");

// Agent-人类协作式TDD
// 人类提出测试思路
const testIdea = "当订单总额超过100美元时,应用10%的折扣";

// Agent生成规范的失败测试(红阶段)
await Task("Create Failing Test", testIdea, "qe-test-generator");

// 人类编写最小化代码(绿阶段)

// Agent提出重构建议
await Task("Suggest Refactorings", { preserveTests: true }, "qe-test-refactorer");

Agent Coordination Hints

Agent协作提示

Memory Namespace

内存命名空间

aqe/tdd/
├── test-plan/*        - TDD session plans
├── red-phase/*        - Failing tests generated
├── green-phase/*      - Implementation code
└── refactor-phase/*   - Refactoring suggestions
aqe/tdd/
├── test-plan/*        - TDD会话计划
├── red-phase/*        - 生成的失败测试
├── green-phase/*      - 实现代码
└── refactor-phase/*   - 重构建议

Fleet Coordination

集群协作

typescript
const tddFleet = await FleetManager.coordinate({
  workflow: 'red-green-refactor',
  agents: {
    testGenerator: 'qe-test-generator',
    testExecutor: 'qe-test-executor',
    qualityAnalyzer: 'qe-quality-analyzer'
  },
  mode: 'sequential'
});

typescript
const tddFleet = await FleetManager.coordinate({
  workflow: 'red-green-refactor',
  agents: {
    testGenerator: 'qe-test-generator',
    testExecutor: 'qe-test-executor',
    qualityAnalyzer: 'qe-quality-analyzer'
  },
  mode: 'sequential'
});

Related Skills

相关技能

  • agentic-quality-engineering - TDD with agent coordination
  • refactoring-patterns - Refactor phase techniques
  • api-testing-patterns - London school for API testing

  • agentic-quality-engineering - 结合Agent协作的TDD
  • refactoring-patterns - 重构阶段技巧
  • api-testing-patterns - 用于API测试的伦敦流派

Remember

要点回顾

Chicago: Test state, use real objects, refactor freely London: Test interactions, mock dependencies, design interfaces first Both: Write the test first, make it pass, refactor
Neither is "right." Choose based on context. Mix as needed. Goal: well-designed, tested code.
With Agents: Agents excel at generating tests, validating green phase, and suggesting refactorings. Use agents to maintain TDD discipline while humans focus on design decisions.
芝加哥流派: 测试状态,使用真实对象,自由重构 伦敦流派: 测试交互,Mock依赖,先设计接口 共同点: 先写测试,让测试通过,再重构
没有绝对“正确”的流派。根据场景选择,按需混合使用。目标:打造设计良好、经过测试的代码。
借助Agent: Agent擅长生成测试、验证绿阶段代码、提出重构建议。使用Agent维持TDD规范,人类聚焦设计决策。