当前位置: 首页 > 面试题库 >

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

欧渝
2023-03-14
问题内容

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

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

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


问题答案:

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.
    必须通过可配置的实例创建方法(工厂或服务定位器)获得要测试的每个类的依赖项,或将其公开以进行依赖项注入。否则,单元测试将无法将依赖关系的模拟实现传递给被测单元。
  2. 由于只能模拟实例方法,因此要进行单元测试的类不能对其依赖项调用任何静态方法,也不能使用任何构造函数实例化它们。

以上内容是从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也无法捕获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在对void方法进行存根时也会遇到语法不一致的情况。例如,您编写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方面的经历非常积极。



 类似资料:
  • 在我的项目中,我们已经有mockito核心依赖项。我想存根静态方法,我需要为其添加mockito内联依赖项。所以想了解它们之间的区别。它们可以共存吗?

  • JMockit 是一组工具和API用以帮助开发人员编写测试程序,该项目完全基于 Java 5 SE 的 java.lang.instrument 包开发,内部使用 ASM 库来修改Java的Bytecode。

  • 问题内容: 我知道这是一个包装类,它包装数字。今天,我看到了另一个主要区别: 我真奇怪! 因此,如果每次使用,我们都必须执行以下操作: 我无法解释为什么Double直接将比较做错了。请为我解释。 问题答案: 而在技术上两个不同的对象和运营商只比较引用。 更好,因为它比较值而不是引用。但是仍然不理想。直接比较浮点值应始终将一些误差(ε)考虑在内()。 注意: 这里的比较会产生,但是比较复杂(内部缓存

  • 问题内容: 我在类中使用 私有静态最终LOGGER 字段,并且我希望 LOGGER.isInfoEnabled() 方法返回 false 。我如何通过使用Mockito或jMockit模拟静态的final字段 我的课是: 其junit为: 当我运行它的结果是: 我是jmockit的新手,我希望上述junit案例能够成功运行。而且我必须使用JMockit或Mockito,不能使用Powermocki

  • 我在我的类中使用私有静态final LOGGER字段,我希望LOGGER.isInfoEnabled()方法返回false。如何使用mockito或jMockit模拟静态final字段 我的课是: 当我运行它时,结果是: 我是jmockit新手,我希望上面的junit案例能够成功运行。我只能用JMockit或mockito,不能用powermockito。请帮帮忙。

  • 问题内容: 我是一名Web开发人员,我想将Web产品移至iPhone。其中一种产品类似于GoogleMaps:在手机屏幕上显示地图,您可以拖动或调整地图大小并查看我们添加到地图中的一些信息。 还有其他类似的产品吗?它们之间有什么区别?我应该选择哪一个? 问题答案: 罗里·布莱斯(Rory Blyth)的答案包含有关两个JavaScript移动框架的一些有效观点。但是,他的要点是不正确的。事实是Ti