EasyMock vs Mockito: design vs maintainability?

RAbraham picture RAbraham · May 19, 2010 · Viewed 31.5k times · Source

One way of thinking about this is: 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.

If we care about the maintainability of tests (easier to read, write and having less brittle tests which are not affected much by change), then Mockito seems a better choice.

My questions are:

  • If you have used EasyMock in large scale projects, do you find that your tests are harder to maintain?
  • What are the limitations of Mockito (other than endo testing)?

Answer

Rrr picture Rrr · Dec 26, 2011

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.