Mockito与JMockit之间的比较 – 为什么Mockito比JMockit投票更好?
我正在调查哪个模拟框架用于我的项目,并已缩小到JMockit和Mockito 。
我注意到Mockito在Stackoverflow上被评选为“ 最好的Java模拟框架 ”。
在比较JMockit的“ 模拟工具比较matrix ”中的特性时, JMockit显示出多种不同的特性。
有没有人有任何具体的信息(而不是意见) Mockito可以做什么不能用JMockit实现,反之亦然?
我想说的竞争是在JMockit和PowerMock之间,然后是Mockito 。
我会留下“普通的”jMock和EasyMock,因为他们只使用代理和CGLIB,不像新的框架那样使用Java 5工具。
jMock也没有一个稳定的版本超过4年。 jMock 2.6.0需要2年的时间才能从RC1到RC2,然后再过2年才能真正发布。
关于Proxy&CGLIB vs仪器:
(EasyMock和jMock)是基于java.lang.reflect.Proxy的,它需要一个接口来实现。 另外,它们支持通过CGLIB子类生成类的模拟对象的创build。 正因为如此,所述类不能是最终的,只有可重写的实例方法才能被模拟。 但是,最重要的是,在使用这些工具时,testing中的代码的依赖关系(即被testing的给定类所依赖的其他类的对象)必须由testing来控制,以便可以将mock实例传递给客户端这些依赖关系。 因此,依赖关系不能简单地用我们想写unit testing的客户类中的新运算符实例化。
最终,传统嘲笑工具的技术限制对生产代码施加以下devise限制:
- 在testing中可能需要嘲笑的每个类必须实现一个单独的接口或不是最终的。
- 要testing的每个类的依赖关系必须通过可configuration的实例创build方法(工厂或服务定位器)获得,或者公开用于dependency injection。 否则,unit testing将无法将依赖关系的模拟实现传递给被测单元。
- 由于只有实例方法可以被模拟,所以被unit testing的类不能调用它们的依赖关系上的静态方法,也不能使用任何的构造函数来实例化它们。
以上是从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更“低级”,迫使用户弄清楚并指定需要准备哪些类进行testing(使用@PrepareForTest({ClassA.class,…})注释),并需要特定的API调用来处理可能存在于生产代码中的各种语言结构:静态方法(mockStatic(ClassA.class)),构造函数(suppress(constructor(ClassXyz.class))),构造函数调用expectNew(AClass.class)),部分模拟(createPartialMock(ClassX.class,“methodToMock”))等
- 有了JMockit期望,所有types的方法和构造函数都是以纯粹的声明方式来模拟的,通过@Mocked注释中的正则expression式来指定部分模拟,或者通过简单的“取消模拟”那些没有预期的成员。 也就是说,开发人员只需为testing类声明一些共享的“模拟字段”,或者为个别testing方法声明一些“本地模拟字段”和/或“模拟参数”(在最后一种情况下,@Mocked注释通常不会被需要)。
- 目前,PowerMock不支持JMockit中提供的一些function,例如支持模拟equals和hashCode,重写方法等等。 另外,在testing执行时,没有等同于JMockit捕获指定基本types的实例和模拟实现的能力,而testing代码本身没有任何实际实现类的知识。
- PowerMock使用自定义类加载器(通常每个testing类一个)来生成修改后的类的类。 如此大量使用自定义类加载器会导致与第三方库冲突,因此有时需要在testing类上使用@PowerMockIgnore(“package.to.be.ignored”)注释。
- 虽然在JDK 1.5上开发时需要将“-javaagent”parameter passing给JVM,但JMockit(通过“Java代理”运行时检测)所使用的机制更简单且更安全。 在JDK 1.6+上(即使部署在较早版本上,也可以始终用于开发),因为JMockit可以使用Attach API按需透明地加载Java代理,所以没有这种要求。
另一个最近的嘲笑工具是Mockito。 虽然它并没有试图克服旧工具(jMock,EasyMock)的限制,但是它引入了一种新的mock行为testing方式。 JMockit也通过validationAPI支持这种替代风格。
JMockit与Mockito
- Mockito依赖对其API的显式调用,以便将logging(当(…))和validation(validation(…))阶段之间的代码分开。 这意味着在testing代码中对模拟对象的任何调用也需要调用模拟API。 另外,当(…)和validation(模拟)…呼叫时,这往往会导致重复。
- 使用JMockit,不存在类似的调用。 当然,我们有新的NonStrictExpectations()和新的Verifications()构造函数调用,但是每个testing(通常)只发生一次,并且完全独立于对模拟方法和构造函数的调用。
- Mockito API在用于调用模拟方法的语法中包含若干不一致之处。 在logging阶段,我们有像(mock.mockedMethod(args))时的调用…而在validation阶段,这个相同的调用将被写为validation(模拟).mockedMethod(参数)。 注意,在第一种情况下,对mockedMethod的调用是直接在模拟对象上进行的,而在第二种情况下则是在由verify(mock)返回的对象上进行的。
- JMockit没有这样的不一致性,因为对模拟方法的调用总是直接在模拟实例本身上进行。 (只有一个例外:为了匹配相同的模拟实例上的调用,使用onInstance(mock)调用,导致像onInstance(mock).mockedMethod(args)这样的代码;但是大多数testing不需要使用这个。 )
- 就像其他依赖方法链接/包装的嘲讽工具一样,当剔除void方法时,Mockito也会遇到不一致的语法。 例如,当你写(mockedList.get(1))。thenThrow(new RuntimeException()); 对于一个非void方法,和doThrow(new RuntimeException())。(mockedList).clear(); 一个虚空的。 使用JMockit,它总是相同的语法:mockedList.clear(); result = new RuntimeException();.
- 另外在使用Mockito间谍方面会出现另一个不一致的情况:“嘲笑”允许真正的方法在窥探的实例上执行。 例如,如果间谍指的是一个空的列表,那么代替写(when spy.get(0))。然后返回(“foo”),你将需要编写doReturn(“foo”)。when(spy).get 0)。 使用JMockit,dynamic模拟function可以为间谍提供类似的function,但是没有这个问题,因为真正的方法只能在重放阶段执行。
- 在EasyMock和jMock中,第一个用于Java的模拟API,重点完全在于模拟对象的预期调用的logging,对于(默认情况下)不允许意外调用的模拟对象。 这些API还提供允许调用的允许意外调用的模拟对象的logging,但这被视为二级function。 另外,有了这些工具,在testing代码被执行之后,就没有办法显式地validation调用了。 所有这些validation都是隐式和自动执行的。
- 在Mockito(也在Unitils Mock)中,采取了相反的观点。 在testing过程中模拟对象的所有调用,无论是否logging,都是允许的,从来没有预料到。 在被testing的代码被执行之后,validation是明确执行的,而不是自动的。
- 这两种方法都太过于极端,因此并不理想。 JMockit Expectations&Verifications是唯一允许开发人员无缝地select每个testing的严格(默认情况下预期)和非严格(默认情况下允许)模拟调用的最佳组合的API。
- 更加清楚的是,Mockito API有以下缺点。 如果您需要validation在testing过程中是否发生了对非空模拟方法的调用,但是testing需要该方法的返回值与返回types的默认值不同,那么Mockitotesting将会有重复的代码: (mock.someMethod())。然后返回(xyz)调用logging阶段,并在validation阶段validation(模拟).someMethod()。 使用JMockit,可以始终logging严格的期望,而不必进行明确的validation。 或者,可以为任何logging的非严格期望指定调用次数约束(次数= 1)(使用Mockito这种约束只能在validation(模拟,约束)调用中指定)。
- Mockito对于validation的语法依然很差,并且对于全面的validation(即,检查所有对模拟对象的调用都进行了明确的validation)。 在第一种情况下,需要创build一个额外的对象,并调用以进行validation:InOrder inOrder = inOrder(mock1,mock2,…)。 在第二种情况下,需要调用像verifyNoMoreInteractions(mock)或verifyZeroInteractions(mock1,mock2)。
- 使用JMockit,您只需编写新的VerificationsInOrder()或新的FullVerifications()而不是新的validation()(或新的FullVerificationsInOrder()来组合这两个需求)。 不需要指定涉及哪些模拟对象。 没有额外的模拟API调用。 作为奖励,通过在有序的validation块内调用unverifiedInvocations(),您可以执行Mockito中根本不可能的与订单有关的validation。
最后,JMockittesting工具包比其他嘲笑工具包有更广泛的范围和更macros大的目标 ,以提供一个完整和复杂的开发人员testing解决scheme。 嘲讽的好API,即使没有人为限制,也不足以有效地创buildtesting。 与IDE无关,易于使用且集成的代码覆盖工具也非常重要,这正是JMockit Coverage所提供的。 随着testing套件规模的扩大,另一个开发者testing工具集将变得更加有用,它是在对生产代码进行本地化更改之后,增量地重新运行testing的能力; 这也包含在Coverage工具中。
(授予,来源可能有偏见,但是…)
我会说与JMockit去。 这是最容易使用,灵活的,适用于几乎所有的情况下,即使是困难的情况下,当你无法控制类testing(或者你不能因为兼容性的原因而破坏它)。
我对JMockit的经验非常积极。
我曾经同Mockito和JMockit合作过,我的经验是:
-
的Mockito:
- 隐式嘲讽( – >更好的可用性,但有未能检测到不允许的模拟方法调用的危险)
- 明确的validation
-
EasyMock的:
- 显式嘲笑
- 隐式validation
-
JMockit:
- 同时支持
-
此外,JMockit的其他好处:
- 如果你正在嘲笑静态方法/构造函数等(比如在没有UT的情况下扩展一个非常古老的代码库),你将有两个select:1)带有Powermock扩展的Mockito / EasyMock或者2)Jmockit
- 内置覆盖报告
我个人更喜欢JMockit,我认为它更加丰富和灵活,但是需要一点点陡峭的学习曲线。 通常有多种方法可以达到相同的嘲讽效果,在devise模拟时需要更多的关注。
我只使用jMockit,因为它是Deencapsultation.class中的reflection库。 我真的很喜欢Mockito的风格,但是我拒绝改变我的代码,浑浊我的API,只有有限的testing框架才能实现。 而且我是testing所有代码的粉丝,所以不能轻易testing私有方法的框架并不是我想要使用的。
我受到这篇文章的影响
在学习过程中,jMockit现在是我主要的mockunit testing框架。
为了方便testing我们遗留的代码库(大量的静态方法调用等),JMockit是非常宝贵的。 [在我的博客文章无耻的插件]
我个人比较喜欢EasyMock 。
在好的,正常的和严格的模拟控制之间转移的能力是我最喜欢的function之一。