【发布时间】:2010-09-06 13:36:03
【问题描述】:
在 Java 中创建模拟对象的最佳框架是什么?为什么?每个框架的优缺点是什么?
【问题讨论】:
标签: java unit-testing mocking
在 Java 中创建模拟对象的最佳框架是什么?为什么?每个框架的优缺点是什么?
【问题讨论】:
标签: java unit-testing mocking
我使用Mockito 取得了很大的成功。
当我尝试学习 JMock 和 EasyMock 时,我发现学习曲线有点陡峭(尽管可能只是我自己)。
我喜欢 Mockito,因为它的语法简单明了,我能够很快掌握。最小语法旨在很好地支持常见情况,尽管有几次我需要做一些更复杂的事情,但我发现我想要的东西得到了支持并且很容易掌握。
这是来自 Mockito 主页的(删节)示例:
import static org.mockito.Mockito.*;
List mockedList = mock(List.class);
mockedList.clear();
verify(mockedList).clear();
没有比这更简单的了。
我能想到的唯一主要缺点是它不会模拟静态方法。
【讨论】:
我是 PowerMock 的创建者,所以显然我必须推荐它! :-)
PowerMock 扩展了 EasyMock 和 Mockito 的能力,具有 mock static methods、最终甚至私有方法的能力。 EasyMock 支持已完成,但 Mockito 插件需要更多工作。我们还计划添加 JMock 支持。
PowerMock 并非旨在取代其他框架,而是可以在其他框架不允许模拟的棘手情况下使用。 PowerMock 还包含其他有用的功能,例如 suppressing static initializers 和构造函数。
【讨论】:
JMockit project site 包含大量当前模拟工具包的比较信息。
特别是查看 feature comparison matrix,它涵盖了 EasyMock、jMock、Mockito、Unitils Mock、PowerMock,当然还有 JMockit。我尽量保持准确和最新。
【讨论】:
我在JMockit 上取得了成功。
这是相当新的,所以它有点原始和记录不足。它使用ASM 动态地重新定义类字节码,因此它可以模拟出所有方法,包括静态、私有、构造函数和静态初始化器。例如:
import mockit.Mockit;
...
Mockit.redefineMethods(MyClassWithStaticInit.class,
MyReplacementClass.class);
...
class MyReplacementClass {
public void $init() {...} // replace default constructor
public static void $clinit{...} // replace static initializer
public static void myStatic{...} // replace static method
// etc...
}
它还有一个 Expectations 接口,允许录制/播放场景:
import mockit.Expectations;
import org.testng.annotations.Test;
public class ExpecationsTest {
private MyClass obj;
@Test
public void testFoo() {
new Expectations(true) {
MyClass c;
{
obj = c;
invokeReturning(c.getFoo("foo", false), "bas");
}
};
assert "bas".equals(obj.getFoo("foo", false));
Expectations.assertSatisfied();
}
public static class MyClass {
public String getFoo(String str, boolean bool) {
if (bool) {
return "foo";
} else {
return "bar";
}
}
}
}
缺点是它需要 Java 5/6。
【讨论】:
您还可以看看使用 Groovy 进行测试。在 Groovy 中,您可以使用“as”运算符轻松模拟 Java 接口:
def request = [isUserInRole: { roleName -> roleName == "testRole"}] as HttpServletRequest
除了这个基本功能之外,Groovy 在模拟前端还提供了更多功能,包括强大的 MockFor 和 StubFor 类。
【讨论】:
我开始使用 EasyMock 的模拟。很容易理解,但是重播步骤有点烦人。 Mockito 删除了这个,也有一个更简洁的语法,因为它看起来可读性是它的主要目标之一。这一点我怎么强调都不为过,因为大多数开发人员会花时间阅读和维护现有代码,而不是创建它。
另一个好处是接口和实现类以相同的方式处理,不像在 EasyMock 中,您仍然需要记住(并检查)才能使用 EasyMock 类扩展。
我最近快速浏览了JMockit,虽然功能清单非常全面,但我认为这样做的代价是生成代码的易读性,并且必须编写更多内容。
对我来说,Mockito 恰到好处,易于编写和阅读,并且可以处理大多数代码需要的大多数情况。将Mockito 与PowerMock 一起使用将是我的选择。
需要考虑的一点是,如果您是自己开发,或者在一个紧密结合的小型团队中开发,您选择的工具可能不适合拥有不同技能水平的开发人员的大公司。在后一种情况下,可读性、易用性和简单性需要更多考虑。如果很多人最终不使用它或不维护测试,那么获得最终的模拟框架是没有意义的。
【讨论】:
我们在工作中大量使用EasyMock 和 EasyMock 类扩展,并且对它非常满意。它基本上为您提供所需的一切。看看文档,有一个很好的例子向你展示了 EasyMock 的所有功能。
【讨论】:
我很早就用过 JMock。我在上一个项目中尝试过 Mockito 并且喜欢它。更简洁,更干净。 PowerMock 涵盖了 Mockito 中没有的所有需求,例如模拟静态代码、模拟实例创建、模拟最终类和方法。所以我拥有完成工作所需的一切。
【讨论】:
我喜欢 JMock,因为您能够设定期望值。这与检查是否在某些模拟库中找到调用方法完全不同。使用 JMock,您可以编写非常复杂的期望。请参阅 jmock cheat-sheat。
【讨论】:
是的,Mockito 是一个很棒的框架。我将它与hamcrest 和Google guice 一起使用来设置我的测试。
【讨论】:
模拟的最佳解决方案是让机器通过基于规范的自动化测试来完成所有工作。对于 Java,请参阅 ScalaCheck 和 Functional Java 库中包含的 Reductio 框架。使用基于规范的自动化测试框架,您可以提供被测方法的规范(关于它的属性应该为真),并且框架会自动生成测试以及模拟对象。
例如,以下属性测试 Math.sqrt 方法以查看任何正数 n 的平方的平方根是否等于 n。
val propSqrt = forAll { (n: Int) => (n >= 0) ==> scala.Math.sqrt(n*n) == n }
当您调用propSqrt.check() 时,ScalaCheck 会生成数百个整数并检查每个整数的属性,还会自动确保很好地覆盖边缘情况。
尽管 ScalaCheck 是用 Scala 编写的,并且需要 Scala 编译器,但使用它来测试 Java 代码还是很容易的。函数式 Java 中的 Reductio 框架是相同概念的纯 Java 实现。
【讨论】:
Mockito 还提供了存根方法、匹配参数(如 anyInt() 和 anyString())、验证调用次数(times(3)、atLeastOnce()、never())、and more.
我还发现 Mockito 是 simple and clean。
我不喜欢 Mockito 的一点是你 can't stub static methods。
【讨论】:
我开始通过 JMock 使用模拟,但最终过渡到使用 EasyMock。 EasyMock 就是这样——更简单——并提供了一种感觉更自然的语法。从那以后我就没有换过。
【讨论】: