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

RAb*_*ham 48 easymock mockito

思考这个的一种方法是:如果我们关心代码的设计则EasyMock的是更好的选择,因为它通过它的期望概念,让反馈给您.

如果我们关心的测试可维护性(更容易读,写和具有不会受到太大变化不太脆测试),那么似乎的Mockito一个更好的选择.

我的问题是:

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

Rrr*_*Rrr 112

我不会说这些框架的测试可读性,大小或测试技术,我相信他们是平等的,但在一个简单的例子,我会告诉你的差异.

鉴于:我们有一个类负责存储某些地方:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我们想测试一下:

的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())));
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,两个测试都是相同的,并且它们都在传递.现在,让我们假设其他人改变了服务实现并尝试运行测试.

新服务实施:

dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)

的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)
Run Code Online (Sandbox Code Playgroud)

在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)
Run Code Online (Sandbox Code Playgroud)

不过,它告诉我,方法没有执行,但只有另一个参数.

为什么Mockito更好?该框架不会在一个地方混合两个职责,当您的测试失败时,您将很容易理解原因.

  • 在此示例中没有理由使用漂亮的模拟。您应该使用普通的模拟(即 `EasyMock.createMock(FileDao.class)`),然后验证的顺序并不重要,并且异常非常清楚:`意外的方法调用 store("path\", "name", java.io.ByteArrayInputStream@58651fd0): store("path", "name", capture(尚未捕获任何内容)): 预期: 1, 实际: 0` (2认同)
  • EasyMock 现在也有类似的 @Mock 注释来自动创建模拟并将它们注入到被测试的类中。 (2认同)

小智 49

如果我们关心代码的设计,那么Easymock是更好的选择,因为它通过其期望概念向您提供反馈

有趣.我发现'期望的概念'使得许多开发人员在测试中提出了越来越多的期望,只是为了满足UnexpectedMethodCall问题.它如何影响设计?

更改代码时,测试不应中断.当功能停止工作时,测试应该中断.如果有人喜欢在任何代码更改发生时打破测试,我建议编写一个测试,断言java文件的md5校验和:)


小智 30

我是EasyMock开发人员,所以有点偏,但我当然在大型项目中使用过EasyMock.

我的观点是,EasyMock测试确实会偶尔发生.EasyMock强制您完整记录您的期望.这需要一些纪律.您应该记录预期的内容,而不是测试方法当前需要的内容.例如,如果在模拟上调用方法的次数无关紧要,请不要害怕使用andStubReturn.另外,如果您不关心参数,请使用anyObject()等等.在TDD中思考可以帮助解决这个问题.

我的分析是,EasyMock测试会更频繁地破解,但是当你想要它们时,Mockito测试不会.我更喜欢我的测试打破.至少我知道我的发展有什么影响.这当然是我个人的观点.

  • 我有兴趣看到一个对比这两种方法的例子...... (5认同)

小智 7

我认为你不应该太在意这件事.Easymock和Mockito都可以配置为'严格'或'不错',唯一的区别是默认Easymock是严格的,Mockito很好.

与所有测试一样,没有硬性规定,您需要平衡测试信心与可维护性.我通常发现某些功能或技术领域需要高度自信,我会使用"严格"的模拟.例如,我们可能不希望多次调用debitAccount()方法!然而,在其他情况下,模拟实际上只是一个存根,所以我们可以测试代码的真正"肉".

在Mockito生命的早期,API兼容性是一个问题,但现在有更多工具支持该框架.Powermock(个人最爱)现在有一个mockito扩展


Law*_*nce 5

老实说,我更喜欢mockito.一直使用EasyMock和unitils两者的组合经常导致像IllegalArgumentException这样的异常:不是接口以及MissingBehaviorExceptions.在这两种情况下,代码和测试代码都非常好.似乎MissingBehaviorException是由于使用createMock创建的模拟对象(使用classextentions !!)确实产生了这个错误.使用@Mock时它确实有效!我不喜欢那种误导行为,对我而言,这清楚地表明它的开发者不知道他们在做什么.一个好的框架应该总是易于使用而不是模棱两可.IllegalArgumentException也是由于EasyMock内部的混合造成的.此外,录音不是我想做的.我想测试我的代码是否抛出异常,并返回预期的结果.结合代码覆盖率对我来说是正确的工具.每当我将1行代码放在上一行或低于前一行时,我不希望我的测试中断,因为这样可以提高性能.有了mockito,没问题.使用EasyMock,即使代码没有被破坏,也会导致测试失败.那很不好.这需要时间,因此需要花钱.您想测试预期的行为.你真的关心事物的顺序吗?我想在极少数情况下你可能会.然后使用Easymock.在其他情况下,我认为使用mockito编写测试的时间会少得多.

亲切的问候劳伦斯