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

121 java unit-testing jmockit mocking mockito

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

我注意到MockitoStackoverflow上被评为" Java最好的模拟框架 ".
在比较JMockit的" Mocking Tool Comparision Matrix "的功能时,似乎JMockit具有多种不同的功能.

有没有人有任何关于Mockito可以用JMockit无法实现的具体信息(不是意见),反之亦然?

Hen*_*wan 137

我说比赛是在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运算符进行实例化.

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

  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(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 vs 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的经历非常积极.

  • 你的答案非常有帮助.我打算简单地使用Mockito,但现在我将首先测试JMockit.不尝试它听起来不错.@machinery:是的,重要的是要看一下趋势,但选择它作为主要标准会限制你进入主流并保护你免受创新. (5认同)
  • 这很奇怪.Mockito和JMockIt在谷歌上分别给了409'和83'点击.肯定JMockIt至少应该出现. (3认同)
  • 我知道这是一个旧线程,但读完这篇评论后,我想我也应该尝试一下。不得不说,从表面上看,JMockit是很有前途的。然而,迄今为止,我发现社区对它的支持非常缺乏。我对 Mock API 的理解遇到了很大的问题,在发布我的问题后,我在 2 天内没有得到任何回复。 (2认同)
  • 答案是8岁了,所以答案里很多东西都不再正确了。一点是,JMockit 从 1.42 开始就需要 `-javaagent` 标志,因为自 Java 9 以来你不能再进行自附加,并且找到合适的解决方法显然不在开发人员的范围内。另一点是,JMockit 不再允许模拟私有方法和构造函数,而 PowerMock 却允许。 (2认同)

Tum*_*mer 23

我和Mockito以及JMockit一起工作,我的经验是:

  • 的Mockito:

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

    • 表达嘲弄
    • 隐式验证
  • JMockit:

    • 支持两者
  • 此外,JMockit的其他好处:

    • 如果你是在模拟静态方法/构造函数等(比如在没有UT的情况下扩展一个非常古老的遗留代码库),你将有两个选择:1)具有Powermock扩展的Mockito/EasyMock或者2)Jmockit
    • 内置报道报告

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


Jos*_*son 15

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

我被这篇文章所左右

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

  • 也许如果您觉得需要测试私有方法,那么您太过关注代码的工作方式,而不是它的工作方式(这是真正的意义,可以通过仅运用其公共方法来确定).另外,我有兴趣阅读那篇文章,但链接已被破坏. (7认同)