Mockito与JMockit之间的比较 - 为什么Mockito投票比JMockit更好? [关闭]

问题

我正在调查哪个模拟框架用于我的项目,并将其缩小到JMockitMockito

我注意到,在Stackoverflow上,Mockitowas投了"the best mock framework for Java"。
在比较JMockit的"Mocking Tool Comparision Matrix"的功能时,似乎Jockit有多个不同的功能。

有没有人有什么特定的信息(不是意见)关于whatMockitocan做什么用JMockit无法实现,反之亦然?


#1 热门回答(130 赞)

我说比赛是在之间JMockitPowerMock,然后是Mockito

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

jMock也没有超过4年的稳定版本。 jMock 2.6.0从RC1到RC2需要2年,然后在实际发布之前需要2年。

关于Proxy&CGLIB vs instrumentation:

(EasyMock和jMock)基于java.lang.reflect.Proxy,它需要实现一个接口。此外,它们还支持通过CGLIB子类生成为类创建模拟对象。因此,所述类不能是最终的,只能模拟可覆盖的实例方法。但最重要的是,在使用这些工具时,测试代码的依赖关系(即,测试中给定类所依赖的其他类的对象)必须由测试控制,以便可以将模拟实例传递给客户端那些依赖。因此,依赖关系不能简单地使用我们想要编写单元测试的客户端类中的new运算符进行实例化。最终,传统模拟工具的技术限制对生产代码施加了以下设计限制:在测试中可能需要模拟的每个类必须实现单独的接口或不是最终的。要测试的每个类的依赖关系必须通过可配置的实例创建方法(工厂或服务定位器)获得,或者暴露用于依赖注入。否则,单元测试将无法将依赖项的模拟实现传递给被测单元。由于只能模拟实例方法,因此要进行单元测试的类不能在其依赖项上调用任何静态方法,也不能使用任何构造函数对它们进行实例化。

以上是从http://jmockit.org/about.html复制而来。此外,它在几个方面比较了它自己(JMockit),PowerMock和Mockito:

现在有其他Java的模拟工具,它们也克服了传统的限制,包括PowerMock,jEasyTest和MockInject。最接近JMockit功能集的是PowerMock,因此我将在此简要评估它(此外,其他两个更受限制,似乎不再积极开发)。 JMockit与PowerMock首先,PowerMock不提供完整的模拟API,而是作为另一个工具的扩展,目前可以是EasyMock或Mockito。对于这些工具的现有用户来说,这显然是一个优势。另一方面,JMockit提供了全新的API,尽管它的主要API(Expectations)类似于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注释通常不会被需要)。目前,PowerMock不支持JMockit中可用的一些功能,例如支持模拟equals和hashCode,重写方法等。此外,没有相当于JMockit能够在测试执行时捕获实例和模拟指定基类型的实现,而测试代码本身并不了解实际的实现类。 PowerMock使用自定义类加载器(通常每个测试类一个),以生成模拟类的修改版本。如此大量使用自定义类加载器可能会导致与第三方库发生冲突,因此有时需要在测试类上使用@PowerMockIgnore("package.to.be.ignored")注释。 JMockit使用的机制(通过"Java代理"运行时检测)更简单,更安全,但它确实需要在JDK 1.5上开发时将"-javaagent"参数传递给JVM;在JDK 1.6(它总是可以用于开发,即使在旧版本上部署)也没有这样的要求,因为JMockit可以使用Attach API按需透明地加载Java代理。另一个最近的嘲弄工具是Mockito。虽然它并没有试图克服旧工具(jMock,EasyMock)的限制,但它确实引入了一种新的行为测试模式。 JMockit还通过Verifications API支持这种替代样式。 JMockit与Mockito Mockito依赖于对其API的显式调用,以便在记录(何时(...))和验证(验证(...))阶段之间分离代码。这意味着在测试代码中对模拟对象的任何调用也需要调用模拟API。此外,这通常会在(...)和验证(模拟)...调用时导致重复。使用JMockit,不存在类似的调用。当然,我们有新的NonStrictExpectations()和新的Verifications()构造函数调用,但它们每次测试只发生一次(通常),并且完全独立于对模拟方法和构造函数的调用。 Mockito API包含用于调用模拟方法的语法中的几个不一致。在记录阶段,我们调用像when(mock.mockedMethod(args))...在验证阶段,这个相同的调用将被写为verify(mock).mockedMethod(args)。请注意,在第一种情况下,对mockedMethod的调用直接在模拟对象上进行,而在第二种情况下,它是在verify(mock)返回的对象上进行的。 JMockit没有这种不一致性,因为对模拟方法的调用总是直接在模拟实例本身上进行。 (仅有一个例外:为了匹配同一个模拟实例上的调用,使用onInstance(模拟)调用,导致代码如onInstance(mock).mockedMethod(args);但是大多数测试都不需要使用它。 )就像其他依赖方法链接/包装的模拟工具一样,Mockito在存在void方法时也会遇到不一致的语法。例如,你写的时候(mockedList.get(1))。thenThrow(new RuntimeException());对于非void方法,和doThrow(new RuntimeException())。when(mockedList).clear();一个无效的。使用JMockit,它始终使用相同的语法:mockedList.clear(); result = new RuntimeException();.使用Mockito间谍时又出现了另一种不一致:"模拟"允许在间谍实例上执行真正的方法。例如,如果spy引用一个空List,那么代替写(spy.get(0))。thenReturn("foo"),你需要编写doReturn("foo")。when(spy).get( 0)。使用JMockit,动态模拟功能提供与间谍类似的功能,但没有此问题,因为实际方法仅在重放阶段执行。在EasyMock和jMock中,Java的第一个模拟API,重点完全在于对模拟对象的预期调用的记录,对于(默认情况下)不允许意外调用的模拟对象。这些API还提供了对允许意外调用的模拟对象的允许调用的记录,但这被视为第二类功能。此外,使用这些工具,在执行测试代码后,无法显式验证对模拟的调用。所有这些验证都是隐式和自动执行的。在Mockito(以及Unitils Mock)中,采取了相反的观点。对测试期间可能发生的模拟对象的所有调用(无论是否记录)都是允许的,从未预料到。在执行测试代码后,将显式执行验证,而不是自动执行。这两种方法都太极端,因此不是最优的。 JMockit Expectations&Verifications是唯一允许开发人员为每个测试无缝选择严格(默认情况下预期)和非严格(默认情况下允许)模拟调用的最佳组合的API。更明确的是,Mockito API具有以下缺点。如果你需要验证在测试期间是否发生了对非void模拟方法的调用,但测试需要该方法的返回值与返回类型的默认值不同,那么Mockito测试将具有重复的代码: a when(mock.someMethod())。thenReturn(xyz)在记录阶段调用,并在验证阶段验证(模拟).someMethod()。使用JMockit,可以始终记录严格的期望,无需明确验证。或者,可以为任何记录的非严格期望指定调用计数约束(times = 1)(使用Mockito,这样的约束只能在verify(模拟,约束)调用中指定)。 Mockito的顺序验证语法很差,并且对于完整的验证(即,检查是否明确验证了对模拟对象的所有调用)。在第一种情况下,需要创建一个额外的对象,并调用验证它:InOrder inOrder = inOrder(mock1,mock2,...)。在第二种情况下,需要进行诸如verifyNoMoreInteractions(mock)或verifyZeroInteractions(mock1,mock2)之类的调用。使用JMockit,你只需编写新内容即可VerificationsInOrder()或新的FullVerifications()而不是新的Verifications()(或新的FullVerificationsInOrder()来组合两个要求)。无需指定涉及哪些模拟对象。没有额外的模拟API调用。作为奖励,通过在有序验证块中调用unverifiedInvocations(),你可以执行在Mockito中根本无法进行的与订单相关的验证。最后,JMockit Testing Toolkit具有比其他模拟工具包更广泛的范围和更宏伟的目标,以提供完整而复杂的开发人员测试解决方案。即使没有人为限制,用于模拟的良好API也不足以高效地创建测试。与IDE无关,易于使用且集成良好的代码覆盖率工具也是必不可少的,而这正是JMockit Coverage旨在提供的。随着测试套件规模的扩大,开发人员测试工具集的另一部分将变得更加有用,它能够在对生产代码进行本地化更改后逐步重新运行测试。这也包含在Coverage工具中。

(授予,来源可能有偏见,但很好......)

我会说JMockit。这是最容易使用,灵活,适用于几乎所有情况甚至是困难的情况和情况,当你无法控制要测试的类(或你不能打破它由于兼容性原因等)。

我对JMockit的经历非常积极。


#2 热门回答(23 赞)

我和Mockito以及JMockit合作过,我对他们的经验是:

  • Mockito:隐式模拟( - >更好的可用性,但有可能无法检测到模拟上不允许的方法调用)显式验证
  • EasyMock:明确嘲笑隐式验证
  • JMockit:支持两者
  • 此外,JMockit的其他好处:如果你正在嘲笑静态方法/构造函数等(例如扩展一个没有UT的非常古老的遗留代码库),你将有两个选择:1)具有Powermock扩展的Mockito / EasyMock或2) Jmockit内置覆盖报告

我个人更喜欢JMockit,我觉得它功能更丰富,更灵活,但需要更加陡峭的学习曲线。通常有多种方法可以实现相同的模拟效果,并且在设计模拟时需要更多的注意。


#3 热门回答(15 赞)

我使用jMockitonly因为它是Deencapsultation.class中的反射库。我其实很喜欢Mockito的风格,但是我拒绝改变我的代码并使我的API变得混乱,因此有限的测试框架可以实现它。而且我是测试我所有代码的粉丝,因此不能轻易测试私有方法的框架不是我想要使用的。

我被this article所左右

经过一个(不可否认的大)学习曲线,jMockit现在是我对模拟的主要单元测试框架。