What is the difference between mocks, stubs and fake objects
虽然有足够的资源,即使是在SO上,但在这些Q / A中,只有两个术语相互比较。
那么,简而言之,他们每个人都是什么? 它们如何相互关联? 或者根本不是吗?
模拟和存根之间的区别非常简单 - 模拟可以使您的测试失败,而存根则不能。这就是全部。此外,您可以将存根视为提供值的东西。如今,假冒只是两者的通用术语(稍后会详述)。
例
让我们考虑一种情况,你必须建立一个通过通信协议发送包的服务(具体细节是无关紧要的)。您只需使用包代码提供服务,剩下的就完成了。鉴于下面的代码片段,您能否确定哪个依赖关系将成为存根以及哪个模拟潜在的单元测试?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public class DistributionService { public double SendPackage(string packageCode) { var contents = this.packageService.GetPackageContents(packageCode); if (contents == null) { throw new InvalidOperationException( "Attempt to send non-exisiting package"); } var package = this.packageBuilder.Build(contents); this.packageDistributor.Send(package); } } |
很容易判断
它与
在这一点上,我们应该得出
快速浏览TDD
Stub是stub,在初始实现中也可以用常量值替换:
1 2 3 | var contents ="Important package"; var package ="<package>Important package</package>"; this.packageDistributor.Send(package); |
这基本上是模拟框架对存根的作用 - 指示它们返回可配置/显式值。老派,手工制作的存根通常就是这样 - 返回恒定值。
显然,这样的代码没有多大意义,但任何曾经做过TDD的人在类开发的早期阶段肯定会看到一堆这样天真的实现。由TDD产生的迭代开发通常有助于识别类的依赖关系的角色。
如今的存根,嘲笑和假货
在这篇文章的开头我提到假的只是一个通用的术语。鉴于mock也可以作为存根(特别是在涉及现代模拟框架时),为了避免混淆,最好将这样的对象称为假的。如今,你可以看到这种趋势在不断增长 - 原始的模拟 - 存根区别正逐渐成为过去,并且使用了更多的通用名称。例如:
- FakeItEasy使用假货
- NSubstitute使用替代品
- Moq使用mock(名称是旧的,但无论是存根还是模拟都没有明显的区别)
参考文献,进一步阅读
- 模拟不是Martin Fowler的存根 - 你可以看到这篇文章在任何存根/模拟问题下被虚拟链接,这是有原因的
- 通过Mark Seemann探索测试双打的连续性 - 所有令人困惑的单元测试术语的概述(如果你认为模拟,假和存根就够了,你可能应该知道还有*虚拟**,间谍,双重和什么不是)
- xunitpatterns.com - 大型单元测试模式书的后备网站,xUnit测试模式:Gerard Meszaros重构测试代码
- Roy Osherove的单元测试艺术 - 优秀的单元测试入门书("模拟可以使测试失败,不存根"是Roy的话,不是我的)
模拟和存根都被称为假对象。 在我看来:
-
Stub用于替换外部依赖项,它使我们的测试运行无异常。 我们必须使用Assert来确定测试是否失败。 Stub仅适用于测试某些功能的结果是否正确
-
模拟更复杂,通常用于测试行为,例如 验证是否是一个被调用的函数
它们通常可以互换,但我认为略有不同。
- 模拟/伪造对象将返回逼真的结果。
- 存根将返回默认的失败/传递值。