Java 重构时Mockito mock对单元测试的影响

Java 重构时Mockito mock对单元测试的影响,java,refactoring,tdd,mockito,Java,Refactoring,Tdd,Mockito,如果我使用注入SUT的对象的mockito mock作为参数,那么在重构过程中,如果代码被重新组织以调用同一mock的另一个非mock方法,会发生什么?我的测试将失败,我必须回去更改我的测试,并为这个新调用设置它们(与重构代码时我希望做的相反) 如果这在重构过程中很常见,那么除了模拟外部资源密集型实体(网络、数据库等)之外,使用模拟还有什么用处呢 我使用mock来模拟对象,因为我的团队似乎喜欢非常深的聚合对象,所以需要花费数小时来设置这些对象 谢谢 我建议你只嘲笑最低限度的需求。Mockito有

如果我使用注入SUT的对象的mockito mock作为参数,那么在重构过程中,如果代码被重新组织以调用同一mock的另一个非mock方法,会发生什么?我的测试将失败,我必须回去更改我的测试,并为这个新调用设置它们(与重构代码时我希望做的相反)

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

我使用mock来模拟对象,因为我的团队似乎喜欢非常深的聚合对象,所以需要花费数小时来设置这些对象


谢谢

我建议你只嘲笑最低限度的需求。Mockito有很多工具可以做到这一点(间谍、以某种方式调用方法时返回特定数据/模拟的能力等),但最终归结为代码中有可测试的“接缝”。如果您还没有这样做,我建议您阅读Michael Feather的书,以获得许多关于如何做到这一点的建议。

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

一般来说,我听说当系统正常运行时,测试或测试夹具会失败,这被称为“脆性”

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

Mockito擅长嘲弄事物:

  • 外部资源密集型依赖项(或其包装器)
  • 接口。这里很少会出错
  • 小API表面。如果您的API表面有一个或两个方法,则不太可能捕捉到您所描述的情况
  • 还不存在的合作者,即使他们有大的API表面。临时脆性试验可在以后修复
Mockito的东西不适合嘲笑:

  • 非常大的API表面和DSL。想象一下用Mockito实现一个构建器模式!更喜欢使用真实对象,或者编写内存中的伪对象或其他双重测试
  • 有状态对象,如数据库和模型对象。假货在这里绝对是更好的选择,如果真的东西不行的话
  • 你无法控制的具体类。此时,诸如
    final
    选项之类的实现细节开始潜入。这可能是创建由您控制的包装器的一个很好的理由
  • 任何具有现有且经过良好测试的实现的内容。当存在真正的实现时,为什么要经历所有这些麻烦并失去测试保真度
值得一提的是,没有两次测试是完全安全的;您的系统可能会缓存、合并、延迟、修改或以其他方式调整与协作者的交互,所有这些都会破坏您编写的几乎所有测试。编写灵活测试的艺术在于尽可能少地依赖实现细节,平衡脆弱性风险与彻底测试系统及其与外部世界交互的需求

综上所述,要直接回答“使用模拟如何有任何用处”的问题,请参阅JB Nizet的:如果你试图制造炸弹雷管,你可能想测试它,但使用真实的东西的成本实在太高了。所涉及的难度(以及测试双精度的最佳选择)将根据所讨论的炸弹是有一百个小触发器,还是只有一个
boom()
方法而有所不同

有关测试双重对象(包括模拟、伪造和虚拟对象)及其优缺点的更多详细信息,请参阅Martin Fowler的文章