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:
Mockito supports both mocks as well as spies. Both spies and mocks perform different functions. Spy creates a partial mock object, whereas mock creates a dummy/ fake (fully mock) object of the real one. Whereas EasyMock supports only mocks.
EasyMock is a mocking framework, JAVA-based library that is used for effective unit testing of JAVA applications. EasyMock is used to mock interfaces so that a dummy functionality can be added to a mock interface that can be used in unit testing.
JMockit will be the chosen option for its fixed-always-the-same structure. Mockito is more or less THE most known so that the community will be bigger. Having to call replay every time you want to use a mock is a clear no-go, so we'll put a minus one for EasyMock. Consistency/simplicity is also important for me.
While Mockito can help with test case writing, there are certain things it cannot do viz:. mocking or testing private, final or static methods. That is where, PowerMockito comes to the rescue. PowerMockito is capable of testing private, final or static methods as it makes use of Java Reflection API.
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.
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 :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With