模拟 ScheduledExecutorService 与“不要模拟你不拥有的类型"哲学

2022-01-14 00:00:00 mocking unit-testing mockito java

模拟 ScheduledExecutorService 会确实使测试我的课程更容易,但根据 mockito 推荐 这似乎是个坏主意,因为模拟类的逻辑可能会发生变化,导致它以不正确的方式使用,但单元测试仍会报告成功.

Mocking ScheduledExecutorService would really make testing my classes easier, but according to the mockito recommendations this seems a bad idea, as the logic of the mocked class can change in a way that it would be used in an incorrect way, but unit tests would still report success.

似乎为它编写一个包装器将是干净"的方式,但我有一种感觉,这只会导致接口的完全重复,这只会让我的代码不那么简单.我想遵循this answer的实用建议,但我不确定ScheduledExecutorService 将始终保持不变.

It seems that writing a wrapper for it would be the "clean" way, but I have a feeling that this would merely result in the complete duplication of an interface, which would just make my code less straightforward. I'd like to follow the practical recommendations of this answer, but I am not sure that the contract of ScheduledExecutorService will always remain the same.

我可以假设现有方法的合同 ScheduledExecutorService(或更一般地说,JRE 库中的任何其他类)永远不会改变?如果没有,如果我在集成测试中测试它的正确使用,同时在单元测试中直接模拟它就足够了吗?

Can I assume that the contract for the existing methods of ScheduledExecutorService (or more generally, any other class in the JRE libs) will never change? If not, is it enough if I test the correct use of it in the integration tests, while still mocking it directly in the unit tests?

推荐答案

与其说是规则,不如说是指导方针;做最有可能导致干净、可靠和不易碎的测试的事情.如您引用的文件中所述:

It's more of a guideline than a rule; do the thing that will most likely result in a clean, reliable, and non-brittle test. As in the document you quoted:

这不是一条硬线,但越过这条线可能会产生影响!(很可能会)

This is not a hard line, but crossing this line may have repercussions! (it most likely will)

这里有一件重要的事情是,不要模拟你不拥有的类型"通常是指 concrete 或 internal 类型,因为它们更有可能在版本之间更改它们的行为,或者获得或丢失 Mockito 的动态覆盖可能无法使用的修饰符,例如 finalstatic.毕竟,如果您要手动子类化第三方类,Java 会抛出编译器错误;Mockito 的语法会在测试运行时对您隐藏它.

One important thing here is that "don't mock types you don't own" usually refers to concrete or internal types, because those are much more likely to change their behavior between versions, or to gain or lose modifiers like final or static that Mockito's dynamic overrides might not pick up on. After all, if you were to subclass a third-party class manually, Java would throw a compiler error; Mockito's syntax would hide that from you until test runtime.

列出我想到的因素:

  • 正如 assylias 在评论中指出的那样,您指的是 Java 接口,它使您免受对 final 方法或方法可见性的常见更改.
  • 该接口文档齐全,专为第三方扩展而设计,这提供了另一个原因,即 Java 不太可能对接口的一般约定进行重大更改.
  • 所讨论的接口在 Java 中是一个非常常用的接口,总体上有很多用户,并且存在很多向后兼容性问题.与较小的库或正在积极开发的库相比,您不太可能受到重大更改的影响.甚至有人可能会说,JRE 与 Java 语言处于这样的锁定步骤,​​您需要担心破坏 syntax 更改而不是破坏 interface 更改.李>
  • As assylias pointed out in the comments, you're referring to a Java interface, which insulates you from common changes to final methods or method visibility.
  • The interface is well-documented and designed for third-party extension, providing yet another reason that Java would be unlikely to make breaking changes to the general contract of the interface.
  • The interface in question is a very well-used interface in Java, which overall has a lot of users, and a lot of backwards-compatibility concerns. It is very unlikely that you'd be subject to breaking changes, compared to a smaller library, or one under active development. One might even say that the JRE is in such lock step to the Java language, you have as much to worry about from breaking syntax changes than from breaking interface changes.

虽然我坚信不要模拟你不拥有的类型"作为一种普遍的启发式或代码味道,但我在这里同意你的观点,即该类型值得模拟,并且——除非你要编写并测试一个完整的实现以用于其他测试——这是您前进的最佳途径.

Though I believe strongly in "don't mock types you don't own" as a general heuristic or code smell, I'd agree with you here that the type is worth mocking, and that—unless you were to write and test a full implementation to be used in other tests—it's the best path forward for you here.

相关文章