我正在研究为项目使用哪种模拟框架,并将其范围缩小到JMockit和Mockito。
我注意到Mockito在Stackoverflow上被评为“ Java最佳模拟框架 ”。
在比较JMockit的“ 模拟工具比较矩阵 ”上的功能时,看来JMockit具有多个不同的功能。
是否有人对Mockito可以做什么(JMockit无法实现)有任何特定信息(无意见),反之亦然?
我正在研究为项目使用哪种模拟框架,并将其范围缩小到JMockit和Mockito。
我注意到Mockito在Stackoverflow上被评为“ Java最佳模拟框架 ”。
在比较JMockit的“ 模拟工具比较矩阵 ”上的功能时,看来JMockit具有多个不同的功能。
是否有人对Mockito可以做什么(JMockit无法实现)有任何特定信息(无意见),反之亦然?
Answers:
2019年9月更新:Spring Boot唯一支持(默认情况下)的模拟框架是Mockito。如果使用Spring,答案很明显。
我想说的是JMockit和PowerMock之间的竞争,然后是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运算符实例化依赖项。
最终,常规模拟工具的技术限制对生产代码施加了以下设计限制:
- 在测试中可能需要模拟的每个类都必须实现单独的接口,或者不是最终接口。
- 必须通过可配置的实例创建方法(工厂或服务定位器)获得要测试的每个类的依赖项,或将其公开以进行依赖项注入。否则,单元测试将无法将依赖项的模拟实现传递给被测单元。
- 由于只能模拟实例方法,因此要进行单元测试的类不能对其依赖项调用任何静态方法,也不能使用任何构造函数实例化它们。
以上内容是从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方面的经历非常积极。
-javaagent
从1.42开始就需要标记,因为自Java 9开始就无法再进行自附加,并且发现合适的解决方法显然不在开发人员的范围之内。另一点是,JMockit不再允许模拟私有方法和构造函数,而PowerMock却禁止了它。
我曾与Mockito和JMockit一起工作,而我的经验是:
Mockito:
EasyMock:
JMockit:
此外,JMockit的其他好处:
我个人更喜欢JMockit,我认为JMockit的功能更丰富,更灵活,但需要一点更陡峭的学习曲线。通常有多种方法可以达到相同的模拟效果,并且在设计模拟时需要格外小心。
我之所以使用jMockit,仅是因为它是Deencapsultation.class中的反射库。我实际上很喜欢Mockito的风格,但是我拒绝更改代码并弄乱我的API,只是为了获得一个有限的测试框架。而且我很喜欢测试我的所有代码,因此,我不能使用无法轻松测试私有方法的框架。
我被这篇文章打动了
在经历了一个(相当大的)学习曲线之后,jMockit现在是我的模拟的主要单元测试框架。