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:
- Create a mock
- call
expect(mock.[method call]).andReturn([result])
for each expected call
- call
mock.[method call]
, then EasyMock.expectLastCall()
for each expected void call
- call
replay(mock)
to switch from "record" mode to "playback" mode
- inject the mock as needed
- call the test method
- 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:
JAVA:
-
public interface Thing {
-
void doSomething
(String parameter
);
-
int doSomethingElse
(String parameter,
int otherParameter
);
-
}
I can create a mock for it via:
JAVA:
-
Thing thing = EasyMock.createMock(Thing.class);
Then I can register expected calls via
JAVA:
-
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.
This entry was posted on Monday, August 13th, 2007 at 2:43am
and is filed under easymock, java, junit.
You can follow any responses to this entry through the RSS 2.0 feed.
You can skip to the end and leave a response. Pinging is currently not allowed.
Source from http://burtbeckwith.com/blog/?p=43