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

发布于 2024-09-02 10:03:07 字数 1432 浏览 7 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

三生殊途 2024-09-09 10:03:08

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

我的观点是 EasyMock 测试确实偶尔会中断。 EasyMock 强制您完整记录您所期望的内容。这需要一些纪律。您应该真正记录预期的内容,而不是测试方法当前需要的内容。例如,如果在模拟上调用某个方法多少次并不重要,请不要害怕使用 andStubReturn。另外,如果您不关心参数,请使用 anyObject() 等。 TDD 思维可以对此有所帮助。

我的分析是,EasyMock 测试会更频繁地中断,但 Mockito 测试不会在您想要的时候中断。我更希望我的测试被打破。至少我知道我的发展带来了哪些影响。这当然是我个人的观点。

I'm an EasyMock developer so a bit partial but of course I've used EasyMock on large scale projects.

My opinion is that EasyMock tests will indeed breaks once in a while. EasyMock forces you to do a complete recording of what you expect. This requires some discipline. You should really record what is expected not what the tested method currently needs. For instance, if it doesn't matter how many time a method is called on a mock, don't be afraid of using andStubReturn. Also, if you don't care about a parameter, use anyObject() and so on. Thinking in TDD can help on that.

My analyze is that EasyMock tests will break more often but Mockito ones won't when you would want them to. I prefer my tests to break. At least I'm aware of what was the impacts of my development. This is of course, my personal point of view.

浸婚纱 2024-09-09 10:03:08

我认为你不应该太担心这个。 Easymock 和 Mockito 都可以配置为“严格”或“良好”,唯一的区别是默认情况下 Easymock 是严格的,而 Mockito 是良好的。

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

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

I don't think you should be too concerned about this. Both Easymock and Mockito can be configured to be 'strict' or 'nice' the only difference is that by default Easymock is strict wheras Mockito is nice.

As with all testing there's no hard and fast rule, you need to balance test confidence against maintainability. I typically find there are certain functional or technical areas that demand a high level of confidence for which I would use 'strict' mocks. For example we probably wouldn't want the debitAccount() method to be called more than once! However there are other cases in which the mock is really little more than a stub so we can test the real 'meat' of the code.

In the early days of Mockito's life API compatibility was a problem but more tools now support the framework. Powermock (a personal favorite) now has a mockito extension

裸钻 2024-09-09 10:03:08

说实话我更喜欢mockito。一直将 EasyMock 与 unitils 结合使用,两者的组合通常会导致异常,例如 IllegalArgumentException: not an interface 以及 MissingBehaviorExceptions。在这两种情况下,代码和测试代码都很好。看来 MissingBehaviorException 是由于使用 createMock (使用类扩展!!)创建的模拟对象确实产生了此错误。当使用@Mock时它确实有效!我不喜欢这种误导行为,对我来说,这清楚地表明它的开发人员不知道他们在做什么。一个好的框架应该总是易于使用且不含糊。 IllegalArgumentException 也是由于 EasyMock 内部的一些混合造成的。而且,录音也不是我想做的。我想测试我的代码是否抛出异常以及它是否返回预期结果。与代码覆盖率相结合对我来说是正确的工具。每当我将一行代码放在前一行代码之上或之下时,我不希望我的测试中断,因为这会提高性能等。使用mockito就没有问题了。使用 EasyMock,即使代码没有损坏,也会导致测试失败。那很糟糕。这需要时间,因此需要金钱。您想要测试预期的行为。你真的关心事情的顺序吗?我想在极少数情况下你可能会这样做。那就用Easymock吧。在其他情况下,我认为您使用mockito 编写测试的时间会少得多。

亲切的问候
劳伦斯

I prefer mockito to be honest. been using EasyMock with unitils and the combination of both oftenly results in exceptions like IllegalArgumentException: not an interface as well as MissingBehaviorExceptions. In both cases though the code and test code are perfectly fine. It appeared that the MissingBehaviorException was due to the fact that mocked objects created with createMock (using classextentions!!) did produce this error. When using @Mock it did work! I do not like that kind of misleading behavior and for me that is a clear indication the developers of it do not know what they are doing. A good framework should always be easy to use and not ambiguous. The IllegalArgumentException was also due to some mingle of EasyMock internals. Also, the recording is not what I want to do. I want to test if my code throws exceptions or not and that it returns the expected results. That in combination with code coverage is the right tool for me. I do not want my tests to break whenever I put 1 line of code above or below the previous one because that improves performance or so. With mockito it is no problem. With EasyMock, it will result tests to fail even though the code is not broken. That is bad. It costs time, thus money. You want to test for expected behavior. Do you really care about the order of things? I suppose in rare occasions you might. Use Easymock then. In other case, I think you'll spend considerably less time using mockito to write your tests.

Kind regards
Lawrence

野心澎湃 2024-09-09 10:03:07

我不会争论这些框架的测试可读性、大小或测试技术,我相信它们是相同的,但通过一个简单的示例,我将向您展示差异。

假设:我们有一个类负责在某处存储某些内容:

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 测试中发生了什么,为什么没有捕获结果?是不是store方法没有执行,但是等一下,是的,为什么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 更好?该框架不会在一个地方混合两种职责,当您的测试失败时,您将很容易理解原因。

I won't argue about test readability, size or testing techniques of these frameworks, I believe they are equal, but on a simple example I'll show you the difference.

Given: We have a class which is responsible for storing something somewhere:

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;
    }
}

and we want to test it:

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);
    }
}

As you can see both test are fairly the same and both of them are passing.
Now, let’s imagine that somebody else changed Service implementation and trying to run tests.

New Service implementation:

dao.store(PATH + separator, NAME, IOUtils.toInputStream(CONTENT));

separator was added at the end of PATH constant

How the tests results will look like right now ? First of all both tests will fail, but with different error messages:

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)

What happened in EasyMock test, why result wasn't captured ? Is store method wasn't executed, but wait a minute, it was, why EasyMock lies to us?

It's because EasyMock mixing two responsibilities in a single line - stubbing and verification. That's why when something is wrong it's hard to understand which part is causing failure.

Of course you can tell me - just change the test and move verify before assertion. Wow, are you serious, developers should keep in mind some magic order inforced by mocking framework?

By the way, it won’t help:

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)

Still, it is saying to me that method was not executed, but it was, only with another parameters.

Why Mockito is better ? This framework doesn't mix two responsibilities in a single place and when your tests will fail, you will easily understand why.

风启觞 2024-09-09 10:03:07

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

有趣的反馈。我发现“期望概念”让许多开发人员投入更多精力和精力。在测试中更多的期望只是为了满足 UnexpectedMethodCall 问题。它如何影响设计?

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

if we care about the Design of the code then Easymock is the better choice as it gives feedback to you by its concept of expectations

Interesting. I found that 'concept of expectations' makes many devs put more & more expectations in the tests only to satisfy UnexpectedMethodCall problem. How does it influence the design?

The test should not break when you change code. The test should break when the feature stops working. If one likes the tests to break when any code change happens I suggest to write a test that asserts the md5 checksum of the java file :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文