何时使用模拟 v.存根,或者两者都不是
本文关键字:或者 两者都 存根 模拟 何时使 | 更新日期: 2023-09-27 17:56:56
我一直在阅读模拟和存根,它们的区别和用途。我仍然有点困惑,但我想我已经掌握了它。
现在我想知道应用程序。我可以看到在测试场景中创建"假"对象的用途,其中实际对象太复杂而无法拖曳以测试一个方面。
但是让我们考虑一下我的应用程序:我正在开发一个计算几何库。我们的库定义了点、线、线段、矢量、多边形和多面体,以及一堆其他对象和所有常用的几何操作。任何给定对象都存储为点或方向列表,或较低级别的对象。但是这些对象都不需要超过几毫秒的时间来生成。
当我测试这个库时,在任何地方使用模拟/存根是否有意义?
现在我们只使用特定的测试用例。我们称它们为存根,但我认为它们不符合存根的技术定义。你认为更好的词汇是什么?"测试用例"?"例子"?
源代码: https://bitbucket.org/Clearspan/geometry-class-library/src
编辑:请注意,我们正在努力实现所有几何对象的不变性,因此仅测试操作结果才有意义,而对初始对象的状态更改没有意义。
mock 和 stub 之间的根本区别在于 mock 会使你的测试失败。存根不能。存根用于保证正确的程序流。它从来都不是断言的一部分。
请注意,模拟也可用于保证流量。换句话说,每个模拟也是一个存根,而存根从来都不是模拟。由于现在这种重叠的职责,你看不到模拟和存根之间的区别,框架设计师会选择更通用的术语(如假的、替代的或包罗万象的模拟)。
这种实现(模拟 - 断言,存根 - 流)有助于我们缩小一些使用场景的范围。从更简单的开始...
模拟
正如我提到的,模拟用于断言。当组件的预期行为是它应该与另一个组件通信时 - 使用 mock。所有这些
emailSender.SendEmail(email);
endOfDayRunner.Run();
jobScheduler.ScheduleJob(jobDetails);
只能通过询问"它是否使用这样那样的参数调用ScheduleJob
"来测试。这是你去模拟的地方。通常这将是模拟的唯一使用场景。
存根
使用存根有点不同。是否使用存根是一个设计问题。一旦你遵循常规的松散耦合、基于依赖注入的设计,最终你会得到很多接口。
现在,在测试中,如何从接口返回值?您要么存根它,要么使用实际实现。每种方法都有其优点和缺点:
- 使用库生成的存根,您的测试将不那么脆弱,但可能需要更多的前期工作(设置存根等)
- 通过实际实现,设置工作已经完成,但是当
Angle
类更改时CoordinateSystem
可能会失败......这种行为是否可取?
是吗?使用哪一个?双!这一切都取决于...
工作单元
我们到达了问题的最终和实际部分。单元测试的范围是什么?单位是什么?CoordinateSystem
可以脱离它的内部工作原理和依赖关系(Angle
、Point
、Line
)吗?它们可以被存根吗?或者更重要的是,他们应该吗?
您始终需要确定您的单位是什么。它是单独CoordinateSystem
还是Angle
,Line
和Point
在其中扮演重要角色?在很多很多情况下,单元将由方法及其周围的生态系统组成,包括域对象、帮助程序类、扩展,有时甚至是其他方法和其他类。
当然,您可以将它们分开并一直存根,但是...真的是你的单位吗?
根据经验,当您需要模拟行为时,请使用 Mocks,当测试中唯一重要的是您与之通信的对象的状态时,请使用存根。
考虑到您对帖子所做的编辑,当您需要接收不可变对象时,请使用存根,但是当您需要调用该对象公开的操作时,然后进行模拟,这样您就不容易由于另一个类实现中的错误而导致测试失败。