关于经典TDD与Mockist测试的困惑求解
厘清单元测试中真实对象 vs Stub/Mock的困惑
这确实是单元测试领域里最容易让人纠结的问题之一,我来帮你拆解清楚核心差异和判断标准:
两种TDD流派的核心分歧
你提到的文章里已经点出了关键:单元测试其实有两种主流流派,它们对“隔离”的理解完全不同:
- 经典TDD(芝加哥派):优先选择使用真实依赖,只有当真实依赖会带来问题时才用Stub/Mock。他们认为,只要依赖是纯逻辑、无外部副作用、快速且稳定的,直接用真实对象能让测试更贴近真实场景,也减少维护替身的成本。比如一个纯内存的数学计算类、字符串处理工具,完全可以直接在测试里实例化使用。
- Mockist TDD(伦敦派):强调完全隔离被测类(SUT),所有外部依赖都必须用Stub/Mock替换。他们的目标是只验证SUT自身的逻辑是否正确,不关心依赖的工作状态,甚至假设依赖是完美的。
文档反例的本质原因
你看到的不推荐代码:
public int GetContosoPrice() { var stockFeed = new StockFeed(); // NOT RECOMMENDED return stockFeed.GetSharePrice("COOO"); }
这里的问题不是“用了真实对象”,而是这个真实对象(StockFeed)大概率带有外部依赖——比如它可能要调用第三方股票API、读取数据库或者访问文件系统。这类依赖会导致测试:
- 运行速度慢(网络请求/DB操作耗时)
- 不稳定(API可能宕机、返回数据随机变化)
- 有副作用(比如触发API调用次数限制、产生测试垃圾数据)
如果StockFeed是一个纯内存的模拟类(比如硬编码返回固定价格),那肯定不会被标记为不推荐。
什么时候用真实对象?什么时候用替身?
给你一个简单的判断框架:
- 用真实对象:当依赖满足以下所有条件时:
- 没有外部副作用(不碰网络、DB、文件、外部服务)
- 运行速度极快(纯内存计算)
- 行为稳定(输入固定则输出固定,不会随机变化)
- 用Stub/Mock:当依赖存在以下任意一种情况时:
- 有外部副作用(需要和外部系统交互)
- 运行缓慢(会拖慢测试套件的执行速度)
- 行为不稳定(输出不可预测)
- 需要验证SUT和依赖的交互逻辑(比如是否调用了某个方法、调用了几次、传入了什么参数)
最后总结
两种流派没有绝对的对错,选择哪种取决于你的测试目标和项目场景:
- 如果追求测试的真实性、减少维护成本,经典TDD更合适;
- 如果需要严格隔离SUT逻辑、快速验证交互,Mockist TDD更高效。
关键是不要教条化——不要觉得“所有依赖都必须Stub/Mock”,也不要随便把带外部副作用的真实对象塞进测试里,根据依赖的特性和测试的目标灵活选择就好。
内容的提问来源于stack exchange,提问作者Imran Azad
相关产品推荐
相关产品推荐

