mock 和 stub 的根本区别在于 mock 会使你的测试失败。存根不能。存根用于保证正确的程序流程。它永远不是断言的一部分。
请注意,mock 也可用于保证流量。换句话说,每个模拟也是一个存根,而存根永远不是模拟。由于现在这些重叠的职责,你看不到 mock 和 stub 之间有太大的区别,框架设计者会使用更通用的术语(如fake、替代或catch-all mock)。
这种实现(mock - assert, stub - flow)帮助我们缩小了一些使用场景。从更简单的开始...
嘲笑
正如我提到的,在断言中使用了模拟。当您的组件的预期行为是它应该与其他组件通信时- 使用模拟。所有那些
emailSender.SendEmail(email);
endOfDayRunner.Run();
jobScheduler.ScheduleJob(jobDetails);
只能通过询问“是否ScheduleJob
使用某些参数调用?”来进行测试。这是你去模拟的地方。通常这将是 mock 的唯一使用场景。
存根
使用 stub 有点不同。是否使用存根是一个设计问题。一旦你遵循常规的松耦合、基于依赖注入的设计,最终你会得到很多接口。
现在,在测试时,如何从接口返回值?你要么存根它,要么使用真正的实现。每种方法都有其优点和缺点:
- 使用库生成的存根,您的测试将不那么脆弱,但可能需要更多的前期工作(设置存根等)
- 对于实际的实现,设置工作已经完成,但是当
Angle
类更改CoordinateSystem
可能失败时......这种行为是否可取?
是吗?使用哪一个?两个都!这一切都取决于...
工作单位
我们到达了问题的最终部分和实际部分。你的单元测试的范围是什么?单位是什么?可以CoordinateSystem
从它的内部工作和依赖(Angle
, Point
, Line
)中分离出来,它们可以被存根吗?或者更重要的是,他们应该是吗?
您总是需要确定您的单位是什么。它是CoordinateSystem
单独的,还是可能Angle
的,Line
并Point
在其中发挥重要作用?在很多很多情况下,单元将由方法及其周围的生态系统组成,包括领域对象、助手类、扩展,有时甚至是其他方法和其他类。
当然,您可以将它们分开并一直存根,但是……它真的是您的单位吗?