Mockito和JMockit的比较 - 为什么Mockito被评为比JMockit更好?

130

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

我注意到在Stackoverflow上,Mockito被评为“Java最佳模拟框架”。在比较JMockit的功能时,可以在JMockit的"模拟工具比较矩阵"上看到,它有多种不同的功能。

是否有任何关于Mockito可以做到而JMockit无法实现,以及反过来的具体信息(不是意见)?


2
也许Mockito的可用性更好,顺便说一句,我认为JMockit并不比Mockito更成熟... - Alois Cochard
45
根据投票数量,显然社区非常需要这个问题的答案。这意味着导致该问题被关闭的该网站的正交化策略需要进行严肃的反思——因为该网站经常因其不对齐的正交性而无法提供所需的答案,从而自己给自己踩了很多次的后脚跟。没有意识到在树的图分析中,有与节点数相同的方式来看待树的正交性。也许是该网站的正交性而非该问题的正交性不对齐。 - Blessed Geek
10
+1 表示不关闭。这些答案具有很大的价值。选择一项技术并不容易,这些问题可以节省很多时间。确实可能没有一个正确的答案,但 StackOverflow 应该容纳那些没有答案的问题。 - mhstnsc
7
我支持那些认为将此问题标记为“不具建设性”是荒谬的观点。这里提供的答案都有"事实、参考资料和/或专业知识"支持。在技术领域中,如果一个问题不能引起一些"辩论、投票或深入讨论",通常几乎没有什么值得问的。此外,提供事实和专业知识依赖于不同的经验,这些差异会导致辩论和深入讨论。 - Jeff Nyman
@Alois - 你能给出具体的例子,说明JMockit相对于Mockito看起来不够成熟吗? - NitinS
5个回答

143

2019年9月更新: Spring Boot默认支持的唯一模拟框架Mockito。如果你使用Spring,答案非常明显。


我认为竞争主要在JMockit和PowerMock之间,然后是Mockito。
我不会考虑"plain" jMock和EasyMock,因为它们仅使用代理和CGLIB,并且不像较新的框架一样使用Java 5 instrumentation。
此外,jMock在4年内没有稳定版本。jMock 2.6.0需要2年时间从RC1到RC2,然后又过了2年才实际发布。
关于代理&CGLIB vs instrumentation:
(EasyMock 和 jMock) 基于 java.lang.reflect.Proxy,需要实现接口。此外,它们通过 CGLIB 子类生成支持为类创建模拟对象。因此,这些类不能是 final 的,只能是可重写的实例方法才能被模拟。最重要的是,在使用这些工具时,测试代码所依赖的其他类的对象(即给定测试类所依赖的对象)必须由测试控制,以便将模拟实例传递给这些依赖项的客户端。因此,我们不能在要编写单元测试的客户端类中使用 new 操作符来实例化依赖项。
最终,传统模拟工具的技术限制对生产代码施加了以下设计限制:
1.每个可能需要在测试中进行模拟的类必须实现一个独立的接口或不是 final 的。
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注释通常不需要)。
JMockit可用的某些功能(例如支持模拟equals和hashCode、重写方法等)目前在PowerMock中不受支持。此外,没有类似于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与Mockito
Mockito依赖于其API的显式调用,以便在记录(when(...))和验证(verify(...))阶段之间分离代码。这意味着,在测试代码中对模拟对象的任何调用也将需要调用模拟API。此外,这通常会导致重复

(尽管信息来源可能存在偏见,但是......)

我建议使用JMockit。它是最易于使用、灵活的,几乎适用于所有情况,即使在难以控制被测试类的情况下(或由于兼容性原因等原因无法打破它)也能正常工作。

我使用JMockit的经验非常积极。


3
这很奇怪。Mockito和JMockit在谷歌上分别给我409和83个结果。毫无疑问,JMockit至少应该出现。 - thoredge
5
你的回答非常有帮助。我本来计划只使用Mockito,但现在我会先试试JMockit。它听起来太好了,不能不尝试一下。 @machinery:是的,关注趋势很重要,但把它作为主要标准会限制你的选择,遮蔽你的创新。 - deamon
2
我知道这是一个旧帖子,但在阅读了这篇评论后,我也想试一试。我必须说,表面上看,JMockit非常有前途。然而,迄今为止,我发现它的社区支持非常缺乏。我对Mock API的理解存在很大问题,在发布了我的问题后,两天内没有得到任何回应。 - Eric B.
1
@rogerio。确实是这样。这是两年前的评论,所以我无法准确回忆当时的情况,但你肯定能够回答所有问题。然而,最大的问题是,大部分回答都是由你提供的,对于这样一个伟大的框架来说,这是一种遗憾。如果有其他人能够像你一样回答和提供见解,那将是非常棒的。这也意味着答案受到你个人带宽的限制,这是可以理解的。 - Eric B.
3
答案是8年前的,因此答案中的许多内容已经不正确。其中一点是,自Java 9以来,您无法再自我附加,因此JMockit自1.42版本起需要-javaagent标志,并且显然找到合适的解决方法不在开发人员的范围内。另一个要点是,JMockit不再允许模拟私有方法和构造函数,而PowerMock则可以。 - Vampire
显示剩余5条评论

24

我使用过Mockito和JMockit,我的经验是:

  • Mockito:

    • 隐式mocking(->更好的可用性,但存在不能检测到不允许在mocks上调用的方法的风险)
    • 显式验证
  • EasyMock:

    • 显式mocking
    • 隐式验证
  • JMockit:

    • 支持两种方式
  • 此外,JMockit的其他优点包括:

    • 如果您模拟静态方法/构造函数等(例如扩展非常老的遗留代码库而没有UT),您将有两个选择:1)具有Powermock扩展的Mockito/EasyMock或2)JMockit
    • 内置覆盖率报告

我个人更喜欢JMockit,我认为它更具有功能丰富性和灵活性,但需要稍微陡峭的学习曲线。通常有多种方法可以实现相同的mocking效果,并需要在设计mocks时更加小心。


@Hendy Irawan 你能帮忙吗?https://stackoverflow.com/questions/63590630/combination-of-java-11-with-jmockit - spandey
@Dave Moten,您能帮忙解答一下这个问题吗?https://stackoverflow.com/questions/63590630/combination-of-java-11-with-jmockit - spandey

15

我之所以使用jMockit,只是因为它在 Deencapsultation.class 中有反射库。实际上,我喜欢Mockito的风格,但我拒绝改变我的代码或混淆我的API,只为了让一个有限的测试框架能够访问它。而且我是一个所有代码都要测试的粉丝,所以一个不能轻易测试私有方法的框架并不是我想要使用的。

我被这篇文章吸引了

尽管(承认)有一个(相当大的)学习曲线,jMockit现在是我主要的单元测试框架。


8
也许,如果你觉得需要测试私有方法,那么你可能过于关注代码是如何工作的,而不是它做了什么(这才是真正的重点,只需通过调用公共方法就可以确定)。此外,我很想读那篇文章,但链接已经失效了。 - codebox
四年过去了,您是否仍将jMockit作为主要的测试框架?我第一次尝试使用它,并遇到了一些问题,但我不知道是我误解了jMockit的工作方式,还是它本身就不存在某个功能。 - Eric B.
jMockit不再支持测试私有方法。Deencapsulation类在1.47版本中被逐渐弃用,然后完全删除。https://jmockit.github.io/changes.html - Pytry
@Joseph Erickson,您能否请检查 https://stackoverflow.com/questions/63590630/combination-of-java-11-with-jmockit? - spandey

4

对于我们遗留代码库(有许多静态方法调用等)的简单测试,JMockit非常宝贵。[在我的博客上文章进行无耻的宣传]


请问您能否检查一下以下链接 https://stackoverflow.com/questions/63590630/combination-of-java-11-with-jmockit? - spandey

0

我个人更喜欢EasyMock
在漂亮、正常和严格的模拟控制之间切换的能力是我最喜欢的功能之一。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接