java - 重构时 Mockito 模拟对单元测试的影响

标签 java refactoring tdd mockito

如果我使用被注入(inject) SUT 的对象的 mockito 模拟作为参数,如果在重构期间代码被重新组织以调用同一模拟的另一个非模拟方法,会发生什么情况?我的测试会失败,我必须返回并更改我的测试并为这个新调用设置它们(与重构代码时我想做的相反)

如果这在重构过程中很常见,那么除了模拟外部资源密集型实体(网络、数据库等)之外,使用模拟还有什么用?

我正在使用模拟来模拟需要数小时才能设置的对象,因为我的团队似乎喜欢非常深的聚合对象。

谢谢!

最佳答案

您说得对,重构可能会破坏依赖于模拟的代码。 Mockito 不知道方法 foo(int start, int end)foo(int start) 完成同样的事情,如果你在重构时在它们之间切换,你的Mockito 模拟很可能会崩溃。 Mockito 确实为未 stub 的调用提供了合理的默认值,例如 0、null 或空列表;然而,许多重构将需要更现实的值。

一般来说,我听说当系统正常运行时测试或测试夹具失败的趋势称之为“脆弱性”。

部分原因是模拟框架的选择:Mockito 最初是 EasyMock 的一个分支,如果调用太多或太少,EasyMock 默认会失败,但 Mockito 会忽略意外调用,否则提供“不错”的默认行为。另一部分取决于您如何使用框架,其中验证不必要的细节(不重要的调用或参数)可能会使模拟比它们必须的更脆弱。

Mockito 擅长模拟的事情:

  • 外部资源密集型依赖项(或其包装器)。
  • 接口(interface)。这里几乎不会出错。
  • 小 API 表面。如果您的 API 表面有一个或两个方法,则不太可能捕捉到您所描述的情况。
  • 尚不存在的合作者,即使他们拥有大量的 API 表面。临时脆性测试可以稍后修复。

Things Mockito 不适合模拟:

  • 非常大的 API 界面和 DSL。想象一下用 Mockito 实现一个 Builder 模式!更喜欢使用真实对象,或者编写内存中的假对象或其他测试替身。
  • 有状态对象,如数据库和模型对象。假货绝对是更好的选择,如果真货不行的话。
  • 您无法控制的具体类。那时,final 的选择等实现细节开始潜入。这可能是创建您可以控制的包装器的一个很好的理由。
  • 任何具有现有且经过良好测试的实现。既然已经有了真正的实现,为什么还要经历所有这些麻烦并失去测试保真度?

值得一提的是,没有一个测试替身是绝对安全的;你的系统可能会缓存、合并、延迟、修改或以其他方式调整与其协作者的交互,所有这些都可能破坏你编写的几乎所有测试替身。编写灵活测试的艺术是依赖尽可能少的实现细节,平衡脆弱性风险与彻底测试系统及其与外界交互的要求。

综上所述,要直接回答“使用模拟有什么用处”这个问题,请参阅 JB Nizet 的 great analogy here :如果您正在尝试制造炸弹雷管,您可能想要测试它,但使用真实的东西的成本实在是太高了。所涉及的难度(以及测试替身的最佳选择)将根据所讨论的炸弹是否具有一百个小触发器或单个 boom() 方法而有所不同。

有关测试替身(包括模拟、伪造和虚拟对象的类别)及其优缺点的更多详细信息,请参阅 Martin Fowler 的文章 "Mocks aren't Stubs" .

关于java - 重构时 Mockito 模拟对单元测试的影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32873928/

相关文章:

java - TDD 仅适用于模型或其他地方

java - Android与Tomcat相互认证

java - Spring Boot安全请求方法 'POST'不支持

c - 对包含大量宏的程序进行单元测试

java - 需要重构帮助 - 具有多个参数的方法

django - 是否有模拟 Django 模型的标准方法?

java - 你的代码覆盖率有多远?

java - Maven:使用其 ftp 地址在本地存储库中安装 jar

java - 将两种方法合并为一种

c# - 如何断言一个 Action 被调用