mcpField
T
tdd-london-chicago

tdd-london-chicago

github.com/proffesor-for-testing测试与质量519119 次安装今天更新
代码仓库

关于 tdd-london-chicago

为开发者和团队提供伦敦学派与芝加哥学派的 TDD 实践指南,帮助选择测试风格并遵循 Red、Green、Refactor 循环。内容涵盖混合使用方式及 Agent 协作提示。

主要功能

  1. 01按代码类型选择测试风格
  2. 02指导 Red、Green、Refactor 循环
  3. 03比较真实对象与模拟对象
  4. 04说明混合测试策略
  5. 05提供 Agent 协作提示

适用场景

  1. 01为新功能采用测试先行开发
  2. 02为遗留代码补充测试覆盖
  3. 03在模拟与真实对象间选择
  4. 04向团队讲解 TDD 实践

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/proffesor-for-testing/agentic-qe --skill tdd-london-chicago

安装后在 Claude Code 中输入/tdd-london-chicago确认已加载。

附带文件schemas/output.jsonscripts/validate-config.json

详细介绍

测试驱动开发:伦敦学派与芝加哥学派

<default_to_action> 在实施 TDD 或选择测试风格时:

  1. 识别代码类型:领域逻辑 → 芝加哥学派,外部依赖 → 伦敦学派
  2. 先编写失败的测试(Red 阶段)
  3. 实现最少的代码使测试通过(Green 阶段)
  4. 在保持测试通过的同时重构(Refactor 阶段)
  5. 为下一个功能重复此循环

快速选择风格:

  • 纯函数/计算 → 芝加哥学派(真实对象、状态验证)
  • 带依赖项的控制器/服务 → 伦敦学派(模拟对象、交互验证)
  • 值对象 → 芝加哥学派(测试最终状态)
  • API 集成 → 伦敦学派(模拟外部服务)
  • 在实践中混合使用两者(控制器使用伦敦学派,领域逻辑使用芝加哥学派)

关键成功因素:

  • 测试驱动设计,而不只是验证设计
  • 先让测试失败,确保它确实在测试某些内容
  • 编写最少的代码——不实现测试范围之外的功能 </default_to_action>

快速参考卡

适用场景
  • 以测试优先的方式启动新功能
  • 为旧代码添加测试覆盖并进行重构
  • 向团队传授 TDD 实践
  • 在模拟对象与真实对象之间进行选择
TDD 循环
阶段操作原则
Red编写失败的测试确认测试失败,并检查错误消息是否清晰
Green编写最少的代码使测试通过不添加额外功能,不要重构
Refactor改进结构保持测试通过,不添加新功能
学派对比
方面芝加哥学派(古典派)伦敦学派(模拟派)
协作者真实对象模拟对象/桩对象
验证方式状态(断言结果)交互(断言调用)
隔离程度较低(集成)较高(仅单元测试)
重构更容易更难(模拟对象会失效)
设计反馈在使用过程中显现从一开始就明确
Agent 协作
  • qe-test-generator:生成两种学派的测试
  • qe-test-implementer:实现最少的代码(Green)
  • qe-test-refactorer:安全重构(Refactor)

芝加哥学派(基于状态)

理念: 通过公共 API 测试可观察行为。让测试贴近使用者的使用方式。

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

芝加哥学派的优势场景:

  • 状态明确的领域逻辑
  • 算法和计算
  • 值对象(Money、Email)
  • 简单的协作关系
  • 学习新的领域

伦敦学派(基于模拟对象)

理念: 隔离测试每个单元。关注对象之间如何协作。

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

伦敦学派的优势场景:

  • 外部集成(数据库、API)
  • 带有副作用的命令模式
  • 复杂工作流
  • 耗时操作(网络、I/O)

混合方法(推荐)

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

常见陷阱

❌ 过度使用模拟对象(伦敦学派)
// BAD - mocking everything
const product = { getName: jest.fn(), getPrice: jest.fn() };

更好的做法: 只模拟外部依赖项。

❌ 模拟内部实现
// BAD - testing private methods
expect(order._calculateSubtotal).toHaveBeenCalled();

更好的做法: 只测试公共行为。

❌ 测试痛点 = 设计痛点
  • 需要很多模拟对象?→ 依赖项太多
  • 难以搭建测试?→ 构造函数做的事情太多
  • 无法脱离数据库进行测试?→ 耦合问题

Agent 辅助的 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", { preserveTests: true }, "qe-test-refactorer");

Agent 协作提示

记忆命名空间
aqe/tdd/
├── test-plan/*        - TDD session plans
├── red-phase/*        - Failing tests generated
├── green-phase/*      - Implementation code
└── refactor-phase/*   - Refactoring suggestions
集群协作
const tddFleet = await FleetManager.coordinate({
  workflow: 'red-green-refactor',
  agents: {
    testGenerator: 'qe-test-generator',
    testExecutor: 'qe-test-executor',
    qualityAnalyzer: 'qe-quality-analyzer'
  },
  mode: 'sequential'
});

相关技能


请记住

芝加哥学派: 测试状态,使用真实对象,自由重构 伦敦学派: 测试交互,模拟依赖项,先设计接口 两者皆可: 先编写测试,让测试通过,再重构

两种方法都没有绝对的“正确”。根据上下文选择,必要时混合使用。目标是编写设计良好且经过测试的代码。

使用 Agent: Agent 擅长生成测试、验证 Green 阶段以及提出重构建议。利用 Agent 保持 TDD 纪律,让人类专注于设计决策。

注意事项

  • Agent 跳过 Red 阶段,同时编写测试和实现——通过先运行测试来落实“测试必须先失败”这一要求,然后再编写代码
  • 伦敦学派过度使用模拟会导致测试十分脆弱,任何重构都会使其失败——应在架构边界处进行模拟,而不是对每个函数都进行模拟
  • 随着集成范围扩大,芝加哥学派的测试会变得缓慢——应严格控制测试边界
  • Agent 默认对所有内容使用 jest.mock()——应优先使用依赖注入来提高可测试性
  • 重构阶段最容易让 Agent 偷工减料——通过确认测试输出完全一致,验证行为没有变化

评论

支持 Markdown