Fun with EasyMock

I’m a big fan of Test-driven development and have been using JUnit for 6 or 7 years (since around v3.4 or v3.5). I’ve written lots of mock objects (from simplistic dummy concrete classes that implement an entire interface, to Proxy-based mocks, to a pretty decent mock JMS library) and played with various mock libraries, but the one I’m sticking with and using almost exclusively is EasyMock.

Reinventing a wheel is great for learning, and I’ve done it a lot. But perhaps in a sign that I’m getting older, I’m more concerned with getting stuff done, so now I tend to lean towards existing solutions when good ones exist. I searched for what people tend to use for mock objects and jMock and EasyMock are popular. I prefer the method-based approach of EasyMock to the string-based approach of jMock, since code changes will cause the tests to break immediately, and the mocks will be refactored along with the rest of the code if I use the IDE’s refactoring features.

The general pattern with creating mocks for a tier of your application is that you’re trusting that that tier is tested and works correctly, so it’s appropriate to have mocks return hard-coded values. We’re testing that the caller of the mocked interface does the right thing with correct (or incorrect) inputs, so mocks help us to focus on what’s being tested in isolation.

I’d always found it difficult to unit test Spring MVC controllers, since it seems like they need a running container, and I don’t want to deal with the hassle of setting up Cactus. Plus it seems somewhat useless to test controllers, since they shouldn’t be doing much that isn’t tested in proper automated functional tests. If they do too much business logic, they should be refactored to push that work out to the services layer, right?

The reality is that we do too much business logic in controllers, and usually it’s lazyness but often it’s being pragmatic. We also tend to often do even more “illegal” activities in controllers, such as writing directly to the response rather than delegating to a View. This is usually the case for XML and JSON generated for Ajax responses. So until these get refactored, they need to be tested.

It’s helpful to have partially-functioning servlet container classes such as HttpServletRequest and HttpServletResponse, and the Spring Mock library (spring-mock.jar) is great for that. But for creating mocks for DAOs, services, and other interface-based dependency-injected resources, EasyMock greatly simplifies things. And with their user-contributed Class Extension, you can even mock out concrete classes.

The syntax takes a while to get used to. Basically the flow that I tend to use is:

  1. Create a mock
  2. call expect(mock.[method call]).andReturn([result]) for each expected call
  3. call mock.[method call], then EasyMock.expectLastCall() for each expected void call
  4. call replay(mock) to switch from “record” mode to “playback” mode
  5. inject the mock as needed
  6. call the test method
  7. call verify(mock) to assure that all expected calls happened

As simple as that may sound, it can look very weird at first. Say I have an interface:

public interface Thing {
   void doSomething(String parameter);
   int doSomethingElse(String parameter, int otherParameter);
}

I can create a mock for it via:

Thing thing = EasyMock.createMock(Thing.class);

Then I can register expected calls via

EasyMock.expect(thing.doSomethingElse("woot", 5)).andReturn(123);
EasyMock.expect(thing.doSomethingElse("fubar", 45)).andReturn(321);

thing.doSomething("p");
EasyMock.expectLastCall();

This says that in my calling code, when the client calls thing.doSomethingElse("woot", 5) to return 123, when the client calls thing.doSomethingElse("fubar", 45) to return 321, and to expect a call to thing.doSomething("p").

I can then inject this mock into the class being tested in place of the real one, trusting that it will return known results. I can then concentrate on assuring that the tested class does the right thing.

Thanks to EasyMock, my productivity for testing services and controllers is way up – and my code coverage percentages are too.

7 Responses to “Fun with EasyMock”

  1. joni says:

    JMock 2 is no longer string based.

  2. Jimadilo says:

    Actually, I find easy mock pretty horrific. It seems like total overkill to solve a really simple problem. It takes about 5 characters and couple of mouse clicks to generate a mock object these days, and it’s reusuable, whereas easymock is complex, and generally a bit non deterministic.

    I expect that I’ll get flamed for this because I’m not towing the OS/Java brigade line.

  3. Tim N. van der Leeuw says:

    Actually I found it very easy, with EasyMock, to write a lot of tests which don’t actually properly test the business-logic but only if you call a particular bit of API in a particular order — but calling that API said nothing of the validity of the business-logic.

    Later I realized that what we really needed was to use EasyMock as stubs, not as mocks, and in some cases also we were really mocking at the wrong level (but setting up mocks at the correct level would have been a hell of a lot of work).

  4. Sébastien says:

    I’ve been using EasyMock for a few weeks now and I must say that its the best mocking library out there.

    At first I gave JMock a try and liked it, but now that I know EasyMock I won’t go back to the (ultra) verbose style of JMock.

    EasyMock is very easy to use and the code as compact as can be, which is great for tests readability.

  5. woosiki says:

    Thank you. This is very useful for me :)

  6. RC says:

    I’m using easymock with spring controller and modelandvew is returning null even after mocking the DAO class.

    • Mario says:

      I had the same problem too.
      I finally found out that you must set the field of your service.

      Ex: ReflectionTestUtils.setField(controller, “daoServiceName”,
      daoServiceClass);

      And also you have to set the daoServiceName = EasyMock.createMock(daoServiceClass.class)

      This will eliminate your nullpointer.

Leave a Reply

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.