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


124

我正在研究为项目使用哪种模拟框架,并将其范围缩小到JMockitMockito

我注意到MockitoStackoverflow上被评为“ Java最佳模拟框架 ”。
在比较JMockit的“ 模拟工具比较矩阵 ”上的功能时,看来JMockit具有多个不同的功能。

是否有人对Mockito可以做什么(JMockit无法实现)有任何特定信息(无意见),反之亦然?


2
也许更好的使用了嘲讽,顺便说一句,我不认为JMockit比Mockito更成熟...
Alois

42
从投票数来看,显然,这个问题的答案受到社区的追捧。这意味着导致该问题被关闭的该站点的正交化策略需要进行认真的重新思考-因为该站点经常踩到能够通过落入其未对齐的正交性来提供所需答案的位置。没有意识到在树的图分析中,查看树的正交性的方法与存在节点的方法一样多。也许这个站点的正交性而不是问题的正交性是错位的。
Blessed Geek 2013年

9
+1为未关闭。这些答案具有大量价值。选择一种技术并不容易,这些问题可以节省很多时间。确实,可能没有一个正确的答案,但是StackOverflow应该可以毫无疑问地解决问题。
mhstnsc '16

6
我将对那些表示将其关闭为“非建设性的”是荒谬的表示支持。此处提供的答案得到“事实,参考和/或专业知识”的支持。技术领域中不征求某些“辩论,民意测验或扩展讨论”的问题通常几乎不值得提出。此外,提供事实和专业知识取决于不同的经验,而这些差异将导致辩论和扩展讨论。
杰夫·尼曼

@Alois-您能举一些具体示例,说明JMockit看起来不如Mockito成熟吗?
NitinS

Answers:


140

2019年9月更新:Spring Boot唯一支持(默认情况下)的模拟框架Mockito。如果使用Spring,答案很明显。


我想说的是JMockitPowerMock之间的竞争,然后是Mockito

我将保留“普通” jMock和EasyMock,因为它们仅使用代理和CGLIB,并且不使用Java 5工具,如较新的框架。

jMock还没有稳定发布超过4年。jMock 2.6.0从RC1升级到RC2需要2年,然后又需要2年才能真正发布。

关于代理和CGLIB与工具:

(EasyMock和jMock)基于java.lang.reflect.Proxy,它需要实现一个接口。此外,它们支持通过CGLIB子类生成为类创建模拟对象。因此,所述类不能是最终的,只能模拟可重写的实例方法。但是,最重要的是,在使用这些工具时,必须由测试控制测试代码的依赖性(即测试中给定类所依赖的其他类的对象),以便可以将模拟实例传递给客户端。这些依赖项。因此,不能简单地用我们要为其编写单元测试的客户端类中的new运算符实例化依赖项。

最终,常规模拟工具的技术限制对生产代码施加了以下设计限制:

  1. 在测试中可能需要模拟的每个类都必须实现单独的接口,或者不是最终接口。
  2. 必须通过可配置的实例创建方法(工厂或服务定位器)获得要测试的每个类的依赖项,或将其公开以进行依赖项注入。否则,单元测试将无法将依赖项的模拟实现传递给被测单元。
  3. 由于只能模拟实例方法,因此要进行单元测试的类不能对其依赖项调用任何静态方法,也不能使用任何构造函数实例化它们。

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

现在,还有其他针对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注释通常不会被需要)。
  • PowerMock当前不支持JMockit中提供的某些功能,例如支持对等式和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还通过Verification API支持这种替代样式。

JMockit对Mockito

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

最后,与其他模拟工具包相比,JMockit测试工具包具有更广泛的范围更宏伟的目标,以便提供完整而复杂的开发人员测试解决方案。即使没有人为的限制,良好的模拟API也不足以有效地创建测试。与IDE无关,易于使用且集成良好的代码覆盖率工具也是必不可少的,这就是JMockit Coverage旨在提供的。开发人员测试工具集的另一部分将随着测试套件的扩展而变得越来越有用,它是在对生产代码进行本地化更改后能够增量地重新运行测试的功能;覆盖率工具中也包含此功能。

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

我想和JMockit一起去。当您无法控制要测试的类(或者由于兼容性等原因而无法破坏它)时,它是最简单易用,灵活的方法,并且适用于几乎所有情况,甚至困难的情况和场景。

我在JMockit方面的经历非常积极。


1
我从未使用过jmockit,但我想在讨论中添加另一个论点:看看所有讨论过的框架的google趋势比较。截至2012年6月6日,与Mockito和EasyMock相比,JMockit甚至没有出现在Google趋势图上。选择框架时,用户数量也很重要。
机械

3
真奇怪。Mockito和JMockIt在Google上分别给了我409'和83'的匹配。当然,JMockIt应该至少出现。
thoredge

5
您的回答非常有帮助。我计划只使用Mockito,但是现在我将首先测试JMockit。最好不要尝试。@machinery:是的,查看趋势很重要,但是选择它作为主要标准将限制您进入主流,并使您无法进行创新。
守护程序

1
我知道这是一个老话题,但是在阅读了这篇评论后,我想我也会尝试一下。我必须说,从表面上看,JMockit是非常有前途的。但是,到目前为止,我发现社区对此非常缺乏支持。我对Mock API的理解遇到了很大的问题,发布问题后的两天内我没有任何回复。
Eric B.

1
答案是8岁,因此答案中的许多内容不再正确。有一点是,JMockit -javaagent从1.42开始就需要标记,因为自Java 9开始就无法再进行自附加,并且发现合适的解决方法显然不在开发人员的范围之内。另一点是,JMockit不再允许模拟私有方法和构造函数,而PowerMock却禁止了它。
吸血鬼

24

我曾与Mockito和JMockit一起工作,而我的经验是:

  • Mockito:

    • 隐式模拟(->更好的可用性,但有可能无法检测到模拟中不允许的方法调用)
    • 明确验证
  • EasyMock:

    • 公开嘲笑
    • 隐式验证
  • JMockit:

    • 同时支持
  • 此外,JMockit的其他好处:

    • 如果您要模拟静态方法/构造函数等(例如在不使用UT的情况下扩展非常旧的旧代码库),则有两种选择:1)具有Powermock扩展名的Mockito / EasyMock或2)Jmockit
    • 内置覆盖率报告

我个人更喜欢JMockit,我认为JMockit的功能更丰富,更灵活,但需要一点更陡峭的学习曲线。通常有多种方法可以达到相同的模拟效果,并且在设计模拟时需要格外小心。


stackoverflow.com/questions/8003278/… “ verifyNoMoreInteractions”我想如果您想使用嘲笑伪造显式嘲笑
rogerdpack 2013年

15

我之所以使用jMockit,是因为它是Deencapsultation.class中的反射库。我实际上很喜欢Mockito的风格,但是我拒绝更改代码并弄乱我的API,只是为了获得一个有限的测试框架。而且我很喜欢测试我的所有代码,因此,我不能使用无法轻松测试私有方法的框架。

我被这篇文章打动了

在经历了一个(相当大的)学习曲线之后,jMockit现在是我的模拟的主要单元测试框架。


7
也许如果您觉得需要测试私有方法,那么您就太在乎代码的工作方式,而不是代码的工作(这是真正的意义,可以通过仅使用其公共方法来确定)。另外,我将有兴趣阅读该文章,但链接已断开。
代码箱

4年后,您是否仍将jMockit用作主要测试框架?我是第一次尝试它,并遇到了一些问题,但是我不知道这是我误解了jMockit的工作原理,还是它只是一个不存在的功能。
Eric B.

jMockit不再支持测试私有方法。逐渐弃用了Deencapsulation类,然后在1.47版中将其完全删除。jmockit.github.io/changes.html
Pytry


0

我个人更喜欢EasyMock
我喜欢的功能之一就是能够在漂亮,正常和严格的模拟控件之间进行切换。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.