Java Mockito 与 JMockit 之间的比较 - 为什么 Mockito 的投票比 JMockit 好?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4105592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Comparison between Mockito vs JMockit - why is Mockito voted better than JMockit?
提问by
I'm investigating which mocking framework to use for my project and have narrowed it down to JMockitand Mockito.
我正在调查我的项目要使用哪个模拟框架,并将其范围缩小到JMockit和Mockito。
I notice that Mockitowas voted "the best mock framework for Java" on Stackoverflow.
In comparing features on JMockit's "Mocking Tool Comparision Matrix" it appears that JMockithas multiple different features.
我注意到Mockito在Stackoverflow 上被评为“ Java 最佳模拟框架”。
在比较JMockit的“模拟工具比较矩阵”上的功能时,似乎JMockit有多个不同的功能。
Does anyone have any specific information (not opinions) on what Mockitocan do which can't be achieved with JMockitand vice versa?
有没有人有任何关于Mockito可以做什么而不能用JMockit实现的具体信息(不是意见),反之亦然?
采纳答案by Hendy Irawan
Update Sep 2019:The only mocking framework supported (by default) by Spring Bootis Mockito. If you use Spring, the answer is quite obvious.
2019 年 9 月更新:Spring Boot 支持(默认)的唯一模拟框架是Mockito。如果你使用 Spring,答案就很明显了。
I'd say the competition is between JMockitand PowerMock, then Mockito.
我想说的是JMockit和PowerMock之间的竞争,然后是Mockito。
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 和 EasyMock,因为它们只使用代理和 CGLIB,并且不像较新的框架那样使用 Java 5 检测。
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.
jMock 也有超过 4 年没有稳定版本。jMock 2.6.0 从 RC1 到 RC2 需要 2 年时间,然后又需要 2 年时间才能真正发布。
Regarding Proxy & CGLIB vs instrumentation:
关于代理和 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:
- Each class which may need to be mocked in a test must either implement a separate interface or not be final.
- The dependencies of each class to be tested must either be obtained through configurable instance creation methods (factories or a Service Locator), or be exposed for dependency injection. Otherwise, unit tests won't be able to pass mock implementations of dependencies to the unit under test.
- Since only instance methods can be mocked, classes to be unit tested cannot call any static methods on their dependencies, nor instantiate them using any of the constructors.
(EasyMock 和 jMock)基于 java.lang.reflect.Proxy,需要实现一个接口。此外,它们支持通过 CGLIB 子类生成为类创建模拟对象。因此,所述类不能是最终的,只能模拟可覆盖的实例方法。然而,最重要的是,当使用这些工具时,被测代码的依赖关系(即给定被测类所依赖的其他类的对象)必须由测试控制,以便模拟实例可以传递给客户端这些依赖项。因此,不能简单地使用我们要为其编写单元测试的客户端类中的 new 运算符实例化依赖项。
最终,传统模拟工具的技术限制对生产代码施加了以下设计限制:
- 每个可能需要在测试中模拟的类必须实现单独的接口或不是最终的。
- 要测试的每个类的依赖项必须通过可配置的实例创建方法(工厂或服务定位器)获取,或者公开以进行依赖项注入。否则,单元测试将无法将依赖项的模拟实现传递给被测单元。
- 由于只能模拟实例方法,要进行单元测试的类不能在其依赖项上调用任何静态方法,也不能使用任何构造函数实例化它们。
The above is copied from http://jmockit.org/about.html. Further, it compares between itself (JMockit), PowerMock, and Mockito in several ways:
以上是从http://jmockit.org/about.html复制的。此外,它以多种方式在自身 (JMockit)、PowerMock 和 Mockito 之间进行比较:
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).
JMockit vs PowerMock
- First of all, PowerMock does not provide a complete API for mocking, but instead works as an extension to another tool, which currently can be EasyMock or Mockito. This is obviously an advantage for existing users of those tools.
- JMockit, on the other hand, provides entirely new APIs, although its main API (Expectations) is similar to both EasyMock and jMock. While this creates a longer learning curve, it also allows JMockit to provide a simpler, more consistent, and easier to use API.
- Compared to the JMockit Expectations API, the PowerMock API is more "low-level", forcing users to figure out and specify which classes need to be prepared for testing (with the @PrepareForTest({ClassA.class, ...}) annotation) and requiring specific API calls to deal with various kinds of language constructs that may be present in the production code: static methods (mockStatic(ClassA.class)), constructors (suppress(constructor(ClassXyz.class))), constructor invocations (expectNew(AClass.class)), partial mocks (createPartialMock(ClassX.class, "methodToMock")), etc.
- With JMockit Expectations, all kinds of methods and constructors are mocked in a purely declarative way, with partial mocking specified through regular expressions in the @Mocked annotation or by simply "un-mocking" the members with no recorded expectations; that is, the developer simply declares some shared "mock fields" for the test class, or some "local mock fields" and/or "mock parameters" for individual test methods (and in this last case the @Mocked annotation often won't be needed).
- Some capabilities available in JMockit, such as support for mocking equals and hashCode, overridden methods, and others, are currently not supported in PowerMock. Also, there is no equivalent to JMockit's ability to capture instances and mock implementations of specified base types as the test executes, without the test code itself having any knowledge of the actual implementation classes.
- PowerMock uses custom class loaders (usually one per test class) in order to generate modified versions of the mocked classes. Such heavy use of custom class loaders can lead to conflicts with third-party libraries, hence the need to sometimes use the @PowerMockIgnore("package.to.be.ignored") annotation on test classes.
- The mechanism used by JMockit (runtime instrumentation through a "Java agent") is simpler and safer, although it does require passing a "-javaagent" parameter to the JVM when developing on JDK 1.5; on JDK 1.6+ (which can always be used for development, even if deploying on an older version) there is no such requirement, since JMockit can transparently load the Java agent on demand by using the Attach 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.
JMockit vs Mockito
- Mockito relies on explicit calls to its API in order to separate code between the record (when(...)) and verify (verify(...)) phases. This means that any invocation to a mock object in test code will also require a call to the mocking API. Additionally, this will often lead to repetitive when(...) and verify(mock)... calls.
- With JMockit, no similar calls exist. Sure, we have the new NonStrictExpectations() and new Verifications() constructor calls, but they occur only once per test (typically), and are completely separate from the invocations to mocked methods and constructors.
- The Mockito API contains several inconsistencies in the syntax used for invocations to mocked methods. In the record phase, we have calls like when(mock.mockedMethod(args))... while in the verify phase this same call will be written as verify(mock).mockedMethod(args). Notice that in the first case the invocation to mockedMethod is made directly on the mock object, while in the second case it is made on the object returned by verify(mock).
- JMockit has no such inconsistencies because invocations to mocked methods are always made directly on the mocked instances themselves. (With one exception only: to match invocations on the same mocked instance, an onInstance(mock) call is used, resulting in code like onInstance(mock).mockedMethod(args); most tests won't need to use this, though.)
- Just like other mocking tools which rely on method chaining/wrapping, Mockito also runs into inconsistent syntax when stubbing void methods. For example, you write when(mockedList.get(1)).thenThrow(new RuntimeException()); for a non-void method, and doThrow(new RuntimeException()).when(mockedList).clear(); for a void one. With JMockit, it's always the same syntax: mockedList.clear(); result = new RuntimeException();.
- Yet another inconsistency occurs in the use of Mockito spies: "mocks" that allow the real methods to be executed on the spied instance. For example, if spy refers to an empty List, then instead of writing when(spy.get(0)).thenReturn("foo") you will need to write doReturn("foo").when(spy).get(0). With JMockit, the dynamic mocking feature provides similar functionality to spies, but without this issue since real methods only get executed during the replay phase.
- In EasyMock and jMock, the first mocking APIs for Java, the focus was entirely on the recording of expected invocations of mocked methods, for mock objects that (by default) do not allow unexpected invocations. Those APIs also provide the recording of allowed invocations for mock objects that do allow unexpected invocations, but this was treated as a second-class feature. Additionally, with these tools there is no way to explicitly verify invocations to mocks after the code under test is exercised. All such verifications are performed implicitly and automatically.
- In Mockito (and also in Unitils Mock), the opposite viewpoint is taken. All invocations to mock objects that may happen during the test, whether recorded or not, are allowed, never expected. Verification is performed explicitly after the code under test is exercised, never automatically.
- Both approaches are too extreme, and consequently less than optimal. JMockit Expectations & Verifications is the only API that allows the developer to seamlessly choose the best combination of strict (expected by default) and non-strict (allowed by default) mock invocations for each test.
- To be more clear, the Mockito API has the following shortcoming. If you need to verify that an invocation to a non-void mocked method happened during the test, but the test requires a return value from that method that is different from the default for the return type, then the Mockito test will have duplicate code: a when(mock.someMethod()).thenReturn(xyz) call in the record phase, and a verify(mock).someMethod() in the verify phase. With JMockit, a strict expectation can always be recorded, which won't have to be explicitly verified. Alternatively, an invocation count constraint (times = 1) can be specified for any recorded non-strict expectation (with Mockito such constraints can only be specified in a verify(mock, constraint) call).
- Mockito has poor syntax for verifications in order, and for full verifications (that is, checking that all invocations to mock objects are explicitly verified). In the first case, an extra object needs to be created, and calls to verify made on it: InOrder inOrder = inOrder(mock1, mock2, ...). In the second case, calls like verifyNoMoreInteractions(mock) or verifyZeroInteractions(mock1, mock2) need to be made.
- With JMockit, you simply write new VerificationsInOrder() or new FullVerifications() instead of new Verifications() (or new FullVerificationsInOrder() to combine both requirements). No need to specify which mock objects are involved. No extra mocking API calls. And as a bonus, by calling unverifiedInvocations() inside an ordered verification block, you can perform order-related verifications that are simply impossible in Mockito.
Finally, the JMockit Testing Toolkit has a wider scopeand more ambitious goalsthan 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.
现在还有其他用于 Java 的模拟工具也克服了传统工具的局限性,例如 PowerMock、jEasyTest 和 MockInject。最接近JMockit的功能集的是PowerMock,这里我简单评价一下(另外两个比较有限,好像已经没有积极开发了)。
JMockit 与 PowerMock
- 首先,PowerMock 没有提供完整的模拟 API,而是作为另一个工具的扩展,目前可以是 EasyMock 或 Mockito。对于这些工具的现有用户来说,这显然是一个优势。
- 另一方面,JMockit 提供了全新的 API,尽管其主要 API(期望)类似于 EasyMock 和 jMock。虽然这会产生更长的学习曲线,但它也允许 JMockit 提供更简单、更一致且更易于使用的 API。
- 与 JMockit Expectations API 相比,PowerMock API 更“低级”,迫使用户弄清楚并指定需要准备哪些类进行测试(使用 @PrepareForTest({ClassA.class, ...}) 批注) 并需要特定的 API 调用来处理可能出现在生产代码中的各种语言构造:静态方法 (mockStatic(ClassA.class))、构造函数 (suppress(constructor(ClassXyz.class)))、构造函数调用 ( expectNew(AClass.class))、部分模拟(createPartialMock(ClassX.class, "methodToMock"))等。
- 使用 JMockit Expectations,所有类型的方法和构造函数都以纯粹的声明方式进行模拟,部分模拟通过 @Mocked 注释中的正则表达式指定,或者简单地“取消模拟”没有记录期望的成员;也就是说,开发人员只需为测试类声明一些共享的“模拟字段”,或者为单个测试方法声明一些“本地模拟字段”和/或“模拟参数”(在最后一种情况下,@Mocked 注释通常不会被需要)。
- JMockit 中可用的某些功能,例如支持模拟 equals 和 hashCode、覆盖方法等,目前在 PowerMock 中不受支持。此外,没有任何等同于 JMockit 在测试执行时捕获指定基类型的实例和模拟实现的能力,而测试代码本身不具备任何实际实现类的知识。
- PowerMock 使用自定义类加载器(通常每个测试类一个)来生成模拟类的修改版本。自定义类加载器的大量使用会导致与第三方库的冲突,因此有时需要在测试类上使用 @PowerMockIgnore("package.to.be.ignored") 注释。
- JMockit 使用的机制(通过“Java 代理”进行运行时检测)更简单、更安全,尽管在 JDK 1.5 上开发时确实需要将“-javaagent”参数传递给 JVM;在 JDK 1.6+(它始终可以用于开发,即使部署在旧版本上)没有这样的要求,因为 JMockit 可以通过使用附加 API 透明地按需加载 Java 代理。
另一个最近的模拟工具是 Mockito。虽然它没有试图克服旧工具(jMock、EasyMock)的局限性,但它确实引入了一种新的模拟行为测试风格。JMockit 还通过 Verifications API 支持这种替代风格。
JMockit 与 Mockito
- Mockito 依赖对其 API 的显式调用,以便在记录 (when(...)) 和验证 (verify(...)) 阶段之间分离代码。这意味着对测试代码中模拟对象的任何调用也需要调用模拟 API。此外,这通常会导致重复 when(...) 和 verify(mock)... 调用。
- 使用 JMockit,不存在类似的调用。当然,我们有 new NonStrictExpectations() 和 new Verifications() 构造函数调用,但它们每次测试只发生一次(通常),并且与对模拟方法和构造函数的调用完全分开。
- Mockito API 在用于调用模拟方法的语法中包含一些不一致的地方。在记录阶段,我们有像 when(mock.mockedMethod(args))... 这样的调用,而在验证阶段,这个相同的调用将被写为 verify(mock).mockedMethod(args)。请注意,在第一种情况下,对 mockedMethod 的调用是直接在模拟对象上进行的,而在第二种情况下,它是在 verify(mock) 返回的对象上进行的。
- JMockit 没有这样的不一致,因为对模拟方法的调用总是直接在模拟实例本身上进行。(只有一个例外:为了匹配同一个模拟实例上的调用,使用了 onInstance(mock) 调用,导致像 onInstance(mock).mockedMethod(args) 这样的代码;不过,大多数测试不需要使用它。 )
- 就像其他依赖方法链/包装的模拟工具一样,Mockito 在存根 void 方法时也会遇到不一致的语法。例如,你写 when(mockedList.get(1)).thenThrow(new RuntimeException()); 对于非空方法,以及 doThrow(new RuntimeException()).when(mockedList).clear(); 对于一个空的。使用 JMockit,它总是相同的语法:mockedList.clear(); 结果 = new RuntimeException();。
- 在 Mockito 间谍的使用中出现了另一个不一致:“模拟”允许在被监视的实例上执行真正的方法。例如,如果 spy 引用一个空列表,那么您需要编写 doReturn("foo").when(spy).get() 而不是编写 when(spy.get(0)).thenReturn("foo") 0)。使用 JMockit,动态模拟功能提供了与间谍类似的功能,但没有这个问题,因为真正的方法只在重放阶段执行。
- 在 EasyMock 和 jMock(Java 的第一个模拟 API)中,重点完全放在记录模拟方法的预期调用上,用于(默认情况下)不允许意外调用的模拟对象。这些 API 还为允许意外调用的模拟对象提供了允许调用的记录,但这被视为第二类功能。此外,使用这些工具无法在执行被测代码后显式验证对模拟的调用。所有此类验证都是隐式和自动执行的。
- 在 Mockito(以及 Unitils Mock)中,采取了相反的观点。测试期间可能发生的所有对模拟对象的调用,无论是否记录,都是允许的,从来没有预料到。验证是在执行被测代码后显式执行的,绝不会自动执行。
- 这两种方法都过于极端,因此都不是最优的。JMockit Expectations & Verifications 是唯一允许开发人员为每个测试无缝选择严格(默认预期)和非严格(默认允许)模拟调用的最佳组合的 API。
- 更明确地说,Mockito API 有以下缺点。如果您需要验证在测试期间是否发生了对非空模拟方法的调用,但测试需要该方法的返回值与返回类型的默认值不同,则 Mockito 测试将具有重复代码:记录阶段的 when(mock.someMethod()).thenReturn(xyz) 调用,以及验证阶段的 verify(mock).someMethod() 调用。使用 JMockit,可以始终记录严格的期望,而不必明确验证。或者,可以为任何记录的非严格期望指定调用计数约束(次数 = 1)(对于 Mockito,此类约束只能在 verify(mock, constraint) 调用中指定)。
- Mockito 对于顺序验证和完整验证(即检查对模拟对象的所有调用是否都经过显式验证)的语法很差。在第一种情况下,需要创建一个额外的对象,并调用对其进行验证: InOrder inOrder = inOrder(mock1, mock2, ...)。在第二种情况下,需要进行诸如 verifyNoMoreInteractions(mock) 或 verifyZeroInteractions(mock1, mock2) 之类的调用。
- 使用 JMockit,您只需编写 new VerificationsInOrder() 或 new FullVerifications() 而不是 new Verifications() (或 new FullVerificationsInOrder() 来组合这两个要求)。无需指定涉及哪些模拟对象。没有额外的模拟 API 调用。作为奖励,通过在有序验证块内调用 unverifiedInvocations(),您可以执行在 Mockito 中根本不可能的与订单相关的验证。
最后,JMockit 测试工具包比其他模拟工具包具有更广泛的范围和更雄心勃勃的目标,以提供完整和复杂的开发人员测试解决方案。一个好的模拟 API,即使没有人为的限制,也不足以有效地创建测试。与 IDE 无关、易于使用且集成良好的代码覆盖率工具也是必不可少的,而这正是 JMockit Coverage 旨在提供的。随着测试套件规模的增长,开发人员测试工具集的另一部分将变得更加有用,即在对生产代码进行本地化更改后增量重新运行测试的能力;这也包含在 Coverage 工具中。
(granted, the source may be biased, but well...)
(当然,来源可能有偏见,但好吧......)
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.
我对 JMockit 的体验非常积极。
回答by Bivas
回答by Jeff Olson
回答by Joseph Erickson
I use jMockit onlybecause of it's reflection libraries in Deencapsultation.class. I actually love Mockito's style, but I refuse to change my code and muddy up my API just so a limited testing framework can get at it. And I'm a fan of testing all my code, so a framework that can't easily test private methods is not what I want to be using.
我使用 jMockit只是因为它是 Deencapsultation.class 中的反射库。我实际上喜欢 Mockito 的风格,但我拒绝更改我的代码并弄乱我的 API,只是为了让有限的测试框架可以实现它。而且我很喜欢测试我所有的代码,所以不能轻易测试私有方法的框架不是我想要使用的。
I was swayed by this article
被这篇文章感动了
After a (admittedly large) learning curve, jMockit is now my main unit testing framework for mocks.
经过(公认的大)学习曲线之后,jMockit 现在是我的主要模拟单元测试框架。
回答by Tumer
I worked with both Mockito and JMockit, and my experience with them is:
我与 Mockito 和 JMockit 一起工作,我对他们的经验是:
Mockito:
- implicit mocking (-> better usability, but has the danger of failing to detect not-allowed method calls on mocks)
- explicit verification
EasyMock:
- explict mocking
- implicit verification
JMockit:
- supports both
Besides, other benefits of JMockit:
- if you're mocking static methods/constructors etc (such as extending a very old legacy code base without UT), you'll have two choices: 1) Mockito/EasyMock with Powermock extension or 2) Jmockit
- built-in coverage report
莫基托:
- 隐式模拟(-> 更好的可用性,但有无法检测到模拟上不允许的方法调用的危险)
- 显式验证
EasyMock:
- 明显的嘲讽
- 隐式验证
JMockit:
- 两者都支持
此外,JMockit 的其他好处:
- 如果您正在模拟静态方法/构造函数等(例如在没有 UT 的情况下扩展非常旧的遗留代码库),您将有两种选择:1)带有 Powermock 扩展的 Mockito/EasyMock 或 2)Jmockit
- 内置覆盖率报告
I personally prefer JMockit, which I think is more feature rich and flexible, but requires a little bit steeper learning curve. There're usually multiple ways to achieve the same mocking effect, and requires more care when designing the mocks.
我个人更喜欢 JMockit,我认为它功能更丰富、更灵活,但需要更陡峭的学习曲线。通常有多种方法可以实现相同的模拟效果,并且在设计模拟时需要更加小心。