在 Java 中创建模拟对象的最佳框架是什么?为什么?每个框架的优缺点是什么?
14 回答
我使用Mockito取得了很好的成功。
当我尝试学习 JMock 和 EasyMock 时,我发现学习曲线有点陡峭(尽管可能只是我自己)。
我喜欢 Mockito,因为它的语法简单明了,我能够很快掌握。最小语法旨在很好地支持常见情况,尽管有几次我需要做一些更复杂的事情,但我发现我想要的东西得到了支持并且很容易掌握。
这是 Mockito 主页上的一个(删节)示例:
import static org.mockito.Mockito.*;
List mockedList = mock(List.class);
mockedList.clear();
verify(mockedList).clear();
没有比这更简单的了。
我能想到的唯一主要缺点是它不会模拟静态方法。
JMockit 项目站点包含大量当前模拟工具包的比较信息。
特别是,查看功能比较矩阵,其中包括 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备忘单。
是的,Mockito 是一个很棒的框架。我将它与hamcrest和Google guice一起使用来设置我的测试。
模拟的最佳解决方案是让机器通过基于规范的自动化测试来完成所有工作。对于 Java,请参阅函数式 Java库中包含的ScalaCheck和Reductio框架。使用基于规范的自动化测试框架,您提供被测方法的规范(关于它的属性应该为真),框架会自动生成测试和模拟对象。
例如,以下属性测试 Math.sqrt 方法以查看任何正数 n 的平方的平方根是否等于 n。
val propSqrt = forAll { (n: Int) => (n >= 0) ==> scala.Math.sqrt(n*n) == n }
当你调用ScalaCheck 时propSqrt.check()
,ScalaCheck 会生成数百个整数并检查你的属性,同时自动确保边缘情况被很好地覆盖。
尽管 ScalaCheck 是用 Scala 编写的,并且需要 Scala 编译器,但使用它来测试 Java 代码还是很容易的。函数式 Java 中的 Reductio 框架是相同概念的纯 Java 实现。
我开始通过 JMock 使用模拟,但最终过渡到使用 EasyMock。EasyMock 就是这样——更简单——并提供了一种感觉更自然的语法。从那以后我就没有换过。