Mocks 不是 Stubs
内容提要
这篇文章讨论了经典主义和模拟主义两种测试风格的差异。经典主义测试使用真实对象和状态验证,而模拟主义测试使用模拟对象和行为验证。文章还讨论了测试与实现的耦合、设计风格和测试隔离等方面的差异。无论选择哪种风格,了解这些差异对于理解测试驱动开发和使用模拟对象框架是有帮助的。
延伸解读
状态验证与行为验证的本质区别
文章通过订单与仓库的例子清晰展示了两种验证方式的差异。状态验证关注执行后对象的状态变化,比如仓库库存是否减少;行为验证则关注对象间的方法调用,比如订单是否以正确参数调用了仓库的hasInventory和remove方法。理解这一区别是掌握两种测试风格的基础,也直接影响测试的编写方式和维护成本。
测试替身的五种类型及其适用场景
文章引用Meszaros的分类,将测试替身分为虚拟对象、伪造对象、存根、间谍和模拟对象。其中只有模拟对象坚持行为验证,其他替身通常使用状态验证。存根提供预设答案,间谍额外记录调用信息。明确这些概念有助于在测试中根据协作关系的复杂程度选择合适的替身,避免混淆mock与stub。
经典TDD与模拟主义TDD的实践权衡
经典TDD倾向于使用真实对象,仅在必要时使用测试替身,常采用自中而外的方式,关注领域模型;模拟主义TDD则对所有有趣行为的协作者使用模拟对象,支持自外而内的开发,强调外部接口设计。两者在测试隔离、固定装置设置和测试与实现的耦合程度上各有优劣,选择取决于团队习惯和具体场景。
模拟主义测试对设计风格的影响
文章指出模拟主义测试者倾向于避免返回值方法,更喜欢在收集对象上执行操作,并遵循“告诉,不要问”原则,这鼓励使用角色接口。同时,模拟测试更容易与实现耦合,可能增加重构的复杂性。了解这些设计倾向有助于开发者有意识地选择测试风格,并权衡其对代码结构的长远影响。
Q&A
经典主义测试和模拟主义测试有什么主要区别?
经典主义测试使用真实对象和状态验证,而模拟主义测试使用模拟对象和行为验证。
什么是Mock对象,它在测试中有什么作用?
Mock对象是模拟真实对象的特例,促进基于行为验证的测试。
使用Mock对象进行测试时需要注意什么?
使用Mock对象时需要设置预期,并验证这些预期是否被调用。
模拟主义测试如何影响设计决策?
模拟主义测试倾向于鼓励使用角色接口和避免返回值的方法,从而影响设计风格。
经典主义测试和模拟主义测试在测试隔离方面有什么不同?
模拟主义测试更容易实现测试隔离,而经典测试可能导致多个测试失败。
在测试驱动开发中,选择经典主义还是模拟主义有什么考虑因素?
选择取决于上下文和协作关系的复杂性,简单关系倾向于经典主义,复杂关系倾向于模拟主义。