EasyMock vs Mockito:设计与可维护性?

时间:2010-05-19 10:50:39

标签: easymock mockito

考虑这个问题的一种方法是:如果我们关心代码的设计,那么EasyMock是更好的选择,因为它通过其期望概念向您提供反馈。

如果我们关心测试的可维护性(更容易阅读,编写并且不易受到变化的影响,那么Mockito似乎是更好的选择。)

我的问题是:

  • 如果您在大型项目中使用过EasyMock,您是否发现您的测试难以维护?
  • Mockito有哪些限制(除了endo测试)?

5 个答案:

答案 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编写测试的时间会少得多。

亲切的问候 劳伦斯