考虑这个问题的一种方法是:如果我们关心代码的设计,那么EasyMock是更好的选择,因为它通过其期望概念向您提供反馈。
如果我们关心测试的可维护性(更容易阅读,编写并且不易受到变化的影响,那么Mockito似乎是更好的选择。)
我的问题是:
答案 0 :(得分:105)
我不会争论这些框架的测试可读性,大小或测试技术,我相信它们是平等的,但在一个简单的例子中,我将向您展示其中的差异。
鉴于:我们有一个类负责存储某些地方:
public class Service {
public static final String PATH = "path";
public static final String NAME = "name";
public static final String CONTENT = "content";
private FileDao dao;
public void doSomething() {
dao.store(PATH, NAME, IOUtils.toInputStream(CONTENT));
}
public void setDao(FileDao dao) {
this.dao = dao;
}
}
我们想测试一下:
的Mockito:
public class ServiceMockitoTest {
private Service service;
@Mock
private FileDao dao;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
service = new Service();
service.setDao(dao);
}
@Test
public void testDoSomething() throws Exception {
// given
// when
service.doSomething();
// then
ArgumentCaptor<InputStream> captor = ArgumentCaptor.forClass(InputStream.class);
Mockito.verify(dao, times(1)).store(eq(Service.PATH), eq(Service.NAME), captor.capture());
assertThat(Service.CONTENT, is(IOUtils.toString(captor.getValue())));
}
}
EasyMock的:
public class ServiceEasyMockTest {
private Service service;
private FileDao dao;
@Before
public void setUp() {
dao = EasyMock.createNiceMock(FileDao.class);
service = new Service();
service.setDao(dao);
}
@Test
public void testDoSomething() throws Exception {
// given
Capture<InputStream> captured = new Capture<InputStream>();
dao.store(eq(Service.PATH), eq(Service.NAME), capture(captured));
replay(dao);
// when
service.doSomething();
// then
assertThat(Service.CONTENT, is(IOUtils.toString(captured.getValue())));
verify(dao);
}
}
正如你所看到的,两个测试都是相同的,并且它们都在传递。 现在,让我们想象其他人改变了服务实现并尝试运行测试。
新服务实施:
dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));
在PATH常量的末尾添加了分隔符
测试结果现在看起来如何?首先,两个测试都会失败,但会出现不同的错误消息:
EasyMock的:
java.lang.AssertionError: Nothing captured yet
at org.easymock.Capture.getValue(Capture.java:78)
at ServiceEasyMockTest.testDoSomething(ServiceEasyMockTest.java:36)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
的Mockito:
Argument(s) are different! Wanted:
dao.store(
"path",
"name",
<Capturing argument>
);
-> at ServiceMockitoTest.testDoSomething(ServiceMockitoTest.java:34)
Actual invocation has different arguments:
dao.store(
"path\",
"name",
java.io.ByteArrayInputStream@1c99159
);
-> at Service.doSomething(Service.java:13)
EasyMock测试中发生了什么,为什么没有捕获结果?商店方法没有被执行,但是等一下,就是为什么EasyMock对我们说谎?
这是因为EasyMock在一行中混合了两个职责 - 存根和验证。这就是为什么当出现问题时,很难理解哪个部分导致失败。
当然你可以告诉我 - 只需更改测试并在断言前移动验证。哇,你是认真的,开发人员应该记住一些由模拟框架强制执行的魔法命令吗?
顺便说一下,它无济于事:
java.lang.AssertionError:
Expectation failure on verify:
store("path", "name", capture(Nothing captured yet)): expected: 1, actual: 0
at org.easymock.internal.MocksControl.verify(MocksControl.java:111)
at org.easymock.classextension.EasyMock.verify(EasyMock.java:211)
但是,它告诉我方法没有执行,但只有另一个参数。
为什么Mockito更好?这个框架不会在一个地方混合两个职责,当你的考试失败时,你很容易理解为什么。
答案 1 :(得分:48)
如果我们关心代码的设计,那么Easymock是更好的选择,因为它通过其期望概念向您提供反馈
有趣。我发现“期望的概念”使许多开发者更多地投入了更多。在测试中更多的期望只是为了满足UnexpectedMethodCall问题。它如何影响设计?
更改代码时,测试不应中断。当功能停止工作时,测试应该中断。如果有人喜欢在任何代码更改发生时中断测试,我建议编写一个断言java文件的md5校验和的测试:)
答案 2 :(得分:29)
我是EasyMock开发人员,所以有点偏,但我当然在大型项目中使用过EasyMock。
我的观点是,EasyMock测试确实会偶尔发生。 EasyMock强制您完整记录您的期望。这需要一些纪律。您应该记录预期的内容,而不是测试方法当前需要的内容。例如,如果在模拟上调用方法的次数无关紧要,请不要害怕使用andStubReturn
。此外,如果您不关心参数,请使用anyObject()
等。在TDD中思考可以提供帮助。
我的分析是,EasyMock测试会更频繁地破解,但是当你想要它们时,Mockito测试不会。我更喜欢我的测试打破。至少我知道我的发展有什么影响。这当然是我个人的观点。
答案 3 :(得分:7)
我认为你不应该太在意这件事。 Easymock和Mockito都可以配置为“严格”或“不错”,唯一的区别是默认情况下Easymock是严格的Mockito很好。
与所有测试一样,没有严格的规则,您需要平衡测试信心与可维护性。我通常发现某些功能或技术领域需要高度自信,我会使用“严格”的模拟。例如,我们可能不希望多次调用debitAccount()方法!然而,在其他情况下,模拟实际上只是一个存根,所以我们可以测试代码的真正“肉”。
在Mockito生命的早期,API兼容性是一个问题,但现在有更多工具支持该框架。 Powermock(个人最爱)现在有一个mockito扩展
答案 4 :(得分:5)
我更喜欢mockito。一直使用EasyMock和unitils两者的组合经常导致像IllegalArgumentException这样的异常:不是接口以及MissingBehaviorExceptions。在这两种情况下,代码和测试代码都非常好。似乎MissingBehaviorException是由于使用createMock创建的模拟对象(使用classextentions !!)确实产生了这个错误。使用@Mock时它确实有效!我不喜欢那种误导行为,对我而言,这清楚地表明它的开发者不知道他们在做什么。一个好的框架应该总是易于使用而不是模棱两可。 IllegalArgumentException也是由于EasyMock内部的混合造成的。此外,录音不是我想做的。我想测试我的代码是否抛出异常,并返回预期的结果。结合代码覆盖率对我来说是正确的工具。每当我将1行代码放在上一行或低于前一行时,我不希望我的测试中断,因为这样可以提高性能。有了mockito,没问题。使用EasyMock,即使代码没有被破坏,也会导致测试失败。那很不好。这需要时间,因此需要花钱。您想测试预期的行为。你真的关心事物的顺序吗?我想在极少数情况下你可能会。然后使用Easymock。在其他情况下,我认为使用mockito编写测试的时间会少得多。
亲切的问候 劳伦斯