Mockito 与 JMockit 之间的比较 - 为什么 Mockito 的投票比 JMockit 好?

2022-01-08 00:00:00 mocking unit-testing mockito java jmockit

我正在研究为我的项目使用哪个模拟框架,并将其范围缩小到 JMockit 和 Mockito.

I'm investigating which mocking framework to use for my project and have narrowed it down to JMockit and Mockito.

我注意到 Mockito 被选为the最好的 Java 模拟框架"在 Stackoverflow 上.
在比较 JMockit 的模拟工具的功能时比较矩阵",JMockit 似乎有多种不同的特征.

I notice that Mockito was voted "the best mock framework for Java" on Stackoverflow.
In comparing features on JMockit's "Mocking Tool Comparision Matrix" it appears that JMockit has multiple different features.

对于 Mockito 可以做什么而 JMockit 无法实现的事情,是否有人有任何具体信息(不是意见),反之亦然?

Does anyone have any specific information (not opinions) on what Mockito can do which can't be achieved with JMockit and vice versa?

推荐答案

2019年9月更新:唯一的Spring Boot 支持的模拟框架(默认) 是 Mockito.如果你使用 Spring,答案就很明显了.

Update Sep 2019: The only mocking framework supported (by default) by Spring Boot is Mockito. If you use Spring, the answer is quite obvious.

我想说的是JMockit和PowerMock之间的竞争,然后是Mockito.

I'd say the competition is between JMockit and PowerMock, then Mockito.

我会留下普通"的 jMock 和 EasyMock,因为它们只使用代理和CGLIB,并且不要像新框架那样使用 Java 5 工具.

I'd leave "plain" jMock and EasyMock because they use only proxy & CGLIB and do not use Java 5 instrumentation like the newer frameworks.

jMock 也有超过 4 年没有稳定的版本.jMock 2.6.0 从 RC1 升级到 RC2 需要 2 年,然后再需要 2 年才能真正发布.

jMock also didn't have a stable release for over 4 years. jMock 2.6.0 required 2 years to go from RC1 to RC2, and then another 2 years before it actually got released.

关于代理和CGLIB 与仪表:

Regarding Proxy & CGLIB vs instrumentation:

(EasyMock 和 jMock)基于 java.lang.reflect.Proxy,这需要一个接口实施的.此外,他们支持创建模拟对象对于通过 CGLIB 子类的类一代.正因为如此,说类不能是最终的,只有可覆盖的实例方法可以是嘲笑.然而,最重要的是,使用这些工具时被测代码的依赖项(即是,其他类的对象哪个给定的类正在测试取决于)必须由控制测试,以便模拟实例可以传递给那些客户依赖关系.因此,依赖不能简单地用客户端类中的新运算符我们要编写单元测试.

(EasyMock and jMock) are based on java.lang.reflect.Proxy, which requires an interface to be implemented. Additionally, they support the creation of mock objects for classes through CGLIB subclass generation. Because of that, said classes cannot be final and only overridable instance methods can be mocked. Most importantly, however, when using these tools the dependencies of code under test (that is, the objects of other classes on which a given class under test depends) must be controlled by the tests, so that mock instances can be passed to the clients of those dependencies. Therefore, dependencies can't simply be instantiated with the new operator in a client class for which we want to write unit tests.

最终,技术限制传统的模拟工具强加以下设计限制生产代码:

Ultimately, the technical limitations of conventional mocking tools impose the following design restrictions on production code:

  1. 在测试中可能需要模拟的每个类都必须实现一个单独的接口或不是最终的.
  2. 每个要测试的类的依赖必须要么获取通过可配置的实例创建方法(工厂或服务定位器),或暴露于依赖注射.否则,单元测试不会能够通过模拟实现对下属单位的依赖测试.
  3. 由于只能模拟实例方法,因此要对类进行单元测试不能调用任何静态方法它们的依赖关系,也不实例化他们使用任何构造函数.

以上内容复制自 http://jmockit.org/about.html .此外,它还通过多种方式在自身 (JMockit)、PowerMock 和 Mockito 之间进行比较:

The above is copied from http://jmockit.org/about.html . Further, it compares between itself (JMockit), PowerMock, and Mockito in several ways:

现在有其他模拟工具Java里面也克服了传统方法的局限,它们之间有 PowerMock、jEasyTest 和模拟注入.最接近的那个JMockit 的功能集是PowerMock,所以我将简要评估一下它在这里(此外,其他两个是更有限,似乎不是不再积极开发).

There are now other mocking tools for Java which also overcome the limitations of the conventional ones, between them PowerMock, jEasyTest, and MockInject. The one that comes closest to the feature set of JMockit is PowerMock, so I will briefly evaluate it here (besides, the other two are more limited and don't seem to be actively developed anymore).

  • 首先,PowerMock没有提供完整的模拟API,而是作为扩展另一种工具,目前可以EasyMock 或 Mockito.这显然是现有用户的优势那些工具.
  • 另一方面,JMockit 提供了全新的 API,尽管它的主要API(期望)是相似的EasyMock 和 jMock.虽然这创造了更长的学习曲线,它还允许 JMockit 提供一个更简单、更一致、更容易使用 API.
  • 与 JMockit Expectations API 相比,PowerMock API 是更低级",迫使用户找出并指定哪些类需要为测试做好准备(与@PrepareForTest({ClassA.class,...}) 注释)并要求特定的 API 调用来处理各种语言结构生产中可能存在的代码:静态方法(mockStatic(ClassA.class)),构造函数(抑制(构造函数(ClassXyz.class))),构造函数调用(expectNew(AClass.class)), 部分模拟(createPartialMock(ClassX.class,"methodToMock")) 等.
  • 有了JMockit Expectations,各种方法和构造函数以纯粹的声明方式嘲笑,通过指定的部分模拟@Mocked 中的正则表达式注释或简单地取消模拟"没有记录的成员期望;也就是开发商简单地声明一些共享的模拟字段"用于测试类,或一些本地模拟字段"和/或模拟参数"用于个别测试方法(在最后一种情况下@Mocked 注释通常不会需要).
  • JMockit 中的一些可用功能,例如对模拟的支持等于和 hashCode,被覆盖方法和其他方法目前还没有PowerMock 支持.此外,还有不等同于 JMockit 的能力捕获实例并模拟指定基础的实现类型作为测试执行,没有测试代码本身有任何实际执行的知识类.
  • PowerMock 使用自定义类加载器(通常每个测试类一个)为了生成修改版本的嘲笑类.如此大量的使用的自定义类加载器可能导致与第三方库冲突,因此有时需要使用@PowerMockIgnore("package.to.be.ignored")测试类的注释.
  • JMockit 使用的机制(通过Java 代理")更简单、更安全,虽然它确实需要通过-javaagent"参数到 JVM 时在 JDK 1.5 上开发;在 JDK 1.6+(它总是可以用于开发,即使部署在旧版本)没有这样的要求,因为 JMockit 可以透明地加载 Java 代理使用 Attach API 来满足需求.

另一个最近的模拟工具是莫基托.虽然没有尝试克服老年人的局限工具(jMock,EasyMock),它确实引入一种新的行为方式用模拟测试.JMockit 也支持这种另类风格,通过 Verifications API.

Another recent mocking tool is Mockito. Although it does not attempt to overcome the limitations of older tools (jMock, EasyMock), it does introduce a new style of behavior testing with mocks. JMockit also supports this alternative style, through the Verifications API.

  • Mockito 依赖对其 API 的显式调用来分离代码记录之间 (when(...)) 和验证(验证(...))阶段.这意味着对模拟的任何调用测试代码中的对象也需要对模拟 API 的调用.此外,这通常会导致重复 when(...) 和验证(模拟)...调用.
  • 使用 JMockit,不存在类似的调用.当然,我们有新的NonStrictExpectations() 和新的Verifications() 构造函数调用,但是它们每次测试只发生一次(通常),并且完全与调用分开模拟方法和构造函数.
  • Mockito API 在用于对模拟方法的调用.在里面记录阶段,我们有这样的电话何时(mock.mockedMethod(args))...而在验证阶段这个相同的调用将被写为验证(模拟).mockedMethod(参数).请注意,在第一种情况下调用 mockedMethod直接在模拟对象上,而在第二种情况是在verify(mock) 返回的对象.
  • JMockit 没有这样的不一致,因为调用总是制作模拟方法直接在模拟实例上他们自己.(只有一个例外:匹配相同的调用模拟实例,一个 onInstance(mock)调用被使用,导致代码像onInstance(mock).mockedMethod(args);大多数测试不需要使用它,不过.)
  • 就像其他依赖方法的模拟工具一样链接/包装,Mockito 也运行存根时语法不一致无效的方法.例如,你写when(mockedList.get(1)).thenThrow(new运行时异常());对于非空方法和 doThrow(newRuntimeException()).when(mockedList).clear();对于一个无效的.使用 JMockit,它是总是相同的语法:mockedList.clear();结果=新运行时异常();.
  • 在使用 Mockito 间谍时还存在另一个不一致之处:模拟"允许真正的方法是在间谍实例上执行.为了例如,如果 spy 指的是一个空的列出,然后而不是写when(spy.get(0)).thenReturn("foo") 你需要写doReturn("foo").when(spy).get(0).和JMockit,动态模拟功能提供类似的功能间谍,但没有这个问题,因为真正的方法只在重播阶段.
  • 在 EasyMock 和 jMock(Java 的第一个模拟 API)中,重点是完全基于预期的记录模拟方法的调用,对于(默认情况下)不模拟的对象允许意外调用.那些API 还提供记录允许对模拟对象的调用确实允许意外调用,但这被视为二等特征.此外,有了这些工具没有办法明确在被测代码被执行.所有这样的隐式执行验证并自动进行.
  • 在 Mockito(以及 Unitils Mock)中,相反的观点是采取.对模拟对象的所有调用在测试期间可能发生的情况,无论是否记录,都允许,没想到.验证是在代码之后显式执行在测试中被行使,从不自动.
  • 这两种方法都过于极端,因此都不是最优的.JMockit 期望与验证是唯一允许开发人员可以无缝选择严格的最佳组合(预期默认情况下)和非严格(允许默认)为每个模拟调用测试.
  • 更清楚地说,Mockito API 有以下缺点.如果你需要验证对 a 的调用非空模拟方法发生在测试,但测试需要一个从该方法返回值是不同于默认的返回类型,然后是 Mockito 测试将有重复的代码:awhen(mock.someMethod()).thenReturn(xyz)在记录阶段调用,以及verify(mock).someMethod() 在验证阶段.使用 JMockit,一个严格的期待总能被记录,不必明确已验证.或者,调用计数约束(次 = 1)可以是指定用于任何记录的非严格期望(与 Mockito 这样的约束只能在一个验证(模拟,约束)调用).
  • Mockito 的语法很差,无法按顺序进行验证验证(即,检查所有对模拟对象的调用都是明确验证).在第一情况下,需要一个额外的对象创建,并调用验证它: InOrder inOrder = inOrder(mock1,模拟2,...).在第二种情况下,调用像 verifyNoMoreInteractions(mock) 或verifyZeroInteractions(mock1, mock2)需要制作.
  • 使用 JMockit,您只需编写 new VerificationsInOrder() 或 newFullVerifications() 而不是 new验证()(或新FullVerificationsInOrder() 结合两个要求).无需指定涉及哪些模拟对象.不额外的模拟 API 调用.而作为一个奖金,通过调用unverifiedInvocations() 内有序的验证块,你可以执行与订单相关的验证这在 Mockito 中根本不可能.

最后,JMockit 测试工具包范围更广并且更有野心目标 比其他模拟工具包,在为了提供一个完整的和复杂的开发人员测试解决方案.一个很好的模拟 API,甚至没有人为的限制,不是足以生产性地创造测试.与 IDE 无关,易于使用,和集成良好的代码覆盖率工具也是必不可少的,这就是JMockit Coverage 旨在提供.开发人员测试的另一部分将变得更有用的工具集随着测试套件规模的扩大,增量重新运行测试的能力在对生产进行局部更改后代码;这也包含在覆盖工具.

Finally, the JMockit Testing Toolkit has a wider scope and more ambitious goals than other mocking toolkits, in order to provide a complete and sophisticated developer testing solution. A good API for mocking, even without artificial limitations, is not enough for productive creation of tests. An IDE-agnostic, easy to use, and well integrated Code Coverage tool is also essential, and that's what JMockit Coverage aims to provide. Another piece of the developer testing toolset which will become more useful as the test suite grows in size is the ability to incrementally rerun tests after a localized change to production code; this is also included in the Coverage tool.

(当然,来源可能有偏见,但好吧......)

(granted, the source may be biased, but well...)

我会说使用 JMockit.当您无法控制要测试的类(或由于兼容性等原因而无法破坏它)时,它最容易使用、灵活,并且适用于几乎所有情况,甚至是困难的情况和场景.

I'd say go with JMockit. It's the easiest to use, flexible, and works for pretty much all cases even difficult ones and scenarios when you can't control the class to be tested (or you can't break it due to compatibility reasons etc.).

我对 JMockit 的体验非常积极.

My experiences with JMockit have been very positive.

相关文章