Mocking July 9, 2005
Posted by andy in : coding , trackbackUp until recently, object mocking is something that I’d heard of a little but didn’t really use. In my last job, there was a small cadre of people who used mocks, but I wasn’t quite sure why they bothered. I have now attained a little more understanding, since one of the things I do now is create mocks for testing just about every day.
If you’re fully familiar with using Mock objects, and why they are so useful, then this post is not for you. Go write some unit tests.
If you aren’t really familiar, or you just like reading my drivel, read on…
First: A little background.
The company I now work for is an XP shop. So we pair on our work, tend to code to interfaces and we write our tests upfront, ala Test Driven Development.
The test-code-refactor cycle is something that I am enjoying immensely, on a professional level. I am sold so far on XP.
There are a few problems in writing unit tests before the class you are testing, but 2 of them are pertinent here.
- Sometimes supporting classes are difficult to setup and configure.
- Sometimes supporting classes just don’t exist.
This is where mocks can help.
Essentially, they fill in the blanks of an interface with a nothing implementation, which you can configure a little and test against. It’s like a stub implementation, but you have a tool that does it all for you. Life becomes easier.
Up until yesterday I thought that mocks were alright, but I wasn’t overly excited by them. The work I had done so far had led me to believe that configuring Mock behaviour could be just as irritating as using the original classes I was mocking.
I now realise that the classes I was testing where just a bit irritating. They could use some refactoring to make them more easily testable.
Now: Learn by example
Let’s say that you wanted to write a class that would do some process when run, but only on normal business days.
You might start with a couple of interfaces like the following…
public interface Calendar {
public boolean isBusinessDay(Date today);
}
public interface Processor {
public void process();
}
The class that you want to write has two obvious behaviours. It should do the processing when it’s a business day, and it should do nothing when it’s not.
So the first thing you’d write is a test stub that looks something like this…
public class TestCalendarBasedLauncher extends TestCase {
public void testLaunchOnBusinessDay() {
}
public void testLaunchOnNotBusinessDay() {
}
}
It does nothing, but it says what we want done, and we know that when it actually runs all of our functionality will be tested.
We can assume that our CalendarBasedLauncher will need something to run and a calendar to base its decisions on, so we add them to the test class. It will also need a date (I’m using Joda for dates here), to run against. We also add a CalendarBasedLauncher variable, to hold our class under test. It isn’t written yet, but it will be eventually.
public class TestCalendarBasedLauncher extends TestCase {
private static final YearMonthDay TODAY = new YearMonthDay();
private Calendar calendar;
private Processor processor;
private CalendarBasedLauncher launcher;
public void testLaunchOnBusinessDay() {
}
public void testLaunchOnNotBusinessDay() {
}
}
The execution that we want to occur here is that when call the launcher, it will check the calendar and, if it is a business day, call process on the processor. Pretty simple stuff.
However, there’s no externally visible change of state that we can use to verify that this occurred and thus, that our code works as expected. ‘process’ might get called, but we’ll never know. Obviously this is a very simple interface and we could stub it out.
Let’s also say that you only had an implementation of your Calendar interface that always went to internet to find out everything it needed. This would require configuration code, network connections and so forth to work. It would be a pain.
Again, this is a very simple interface so stubbing would be simple. Instead, I’m going to mock both of them. Enter EasyMock.
EasyMock will generate implementations of our interfaces for us, containing behaviour that we define AND it will verify that the behaviour we wanted actually occurred.
First we add a couple of controllers to our test. These will manage the behaviour of our calendar and processor mocks.
private MockControl calendarControl;
private MockControl processorControl;
We now need to configure them in our setUp() (which we will also add).
public void setUp() {
calendarControl = MockControl.createControl(Calendar.class);
calendar = (Calendar) calendarControl.getMock();
processorControl = MockControl.createControl(Processor.class);
processor = (Processor) processorControl.getMock();
launcher = new CalendarBasedLauncher(calendar, processor);
}
While we’re at it, we also instantiate our launcher in the way that we expect it to work. It still doesn’t actually exist. We’re just pretending that it does while we define its behaviour through tests.
The next step is to fill in our tests.
public void testLaunchOnBusinessDay() {
calendarControl.expectAndReturn(calendar.isBusinessDay(TODAY), true);
processor.process();
calendarControl.replay();
processorControl.replay();
launcher.launch();
calendarControl.verify();
processorControl.verify();
}
In this test we are proving that our launcher works on a business day, so we make sure that the call to calendar.isBusinessDay will return true.
That’s what the first line does. It sets up the calendar mock to return true when calendar.isBusinessDay(TODAY) is called.
The second line tells the processor mock to expect a call to its process method, which is what we expect to happen. On a business day, the launcher should call process().
The two .replay() lines tell the mocks that we’re done configuring and now we want them to behave as requested.
We then call our launcher, which will perform its duties (when we write them).
The two .verify() lines tell the mocks to check whether they were executed as expected. If the method calls they were expecting didn’t happen (or too many calls happened), they will make the appropriate assertion failures through JUnit and the test will fail.
We can now similarly fill in the test for when it is not a business day.
public void testLaunchOnNotBusinessDay() {
calendarControl.expectAndReturn(calendar.isBusinessDay(TODAY), false);
calendarControl.replay();
processorControl.replay();
launcher.launch();
calendarControl.verify();
processorControl.verify();
}
In this case we want false to be returned from calendar.isBusinessDay(TODAY) and we don’t expect ANY calls to process at all. If any occur, the mock will complain during its verification.
The last 6 lines are identical, so they can easily be refactored out of the test methods.
Our test class now looks something like this…
public class TestCalendarBasedLauncher extends TestCase {
private static final Date TODAY = new Date();
private Calendar calendar;
private MockControl calendarControl;
private Processor processor;
private MockControl processorControl;
private CalendarBasedLauncher launcher;
public void setUp() {
calendarControl = MockControl.createControl(Calendar.class);
calendar = (Calendar) calendarControl.getMock();
processorControl = MockControl.createControl(Processor.class);
processor = (Processor) processorControl.getMock();
launcher = new CalendarBasedLauncher(calendar, processor);
}
public void testLaunchOnBusinessDay() {
calendarControl.expectAndReturn(calendar.isBusinessDay(TODAY), true);
processor.process();
doTest();
}
public void testLaunchOnNotBusinessDay() {
calendarControl.expectAndReturn(calendar.isBusinessDay(TODAY), false);
doTest();
}
private void doTest() {
calendarControl.replay();
processorControl.replay();
launcher.launch();
calendarControl.verify();
processorControl.verify();
}
Kinda simple. I don’t like these multiple calls on the configuration, replay and verification of mocks, but that can be sorted out using reflection (which is an exercise for the reader :)) .
The rest is trivial, we just need to write our CalendarBasedLauncher class, which is really quite trivial…
public class CalendarBasedLauncher {
private final Calendar calendar;
private final Processor processor;
public void launch() {
if (calendar.isBusinessDay(new YearMonthDay())) {
processor.process();
}
}
}
That’s all there is to it.
Now this is a trivial example obviously, but I hope it points out the usefulness of this concept. I didn’t really get it before this week but I’m quite sold on the idea now.
Unfortunately the amount of repeated crap one has to go through to create, verify and replay the mocks is appalling. For every object you mock there is a minimum of 4 lines of code required + 2 variable declarations. I find this to be unnecessary clutter.
I highly advise anyone planning on using EasyMock to create a helper class that sets the appropriate controllers and mocks using reflection. You’ll still probably need 2 variable declarations per mock (depending on how you do it), but the setup, verification and replay can be done with just 3 lines of code for however many mocks you’re using. Which is nicer I think.
That is all.
Apologies to Ben for nerding out.
Comments»
Okay… anyone who can tell me why WordPress is being so irritating with my pre and code tags will be forever hailed as a saviour.
No matter whether I use just pre, just code or any combination of the two, my output is not as I entered it.
Furthermore (from looking at the page source), it seems that WordPress is stripping my spaces when it renders the post.
Unacceptable!!
It’s stripping the spaces because all it is doing is dumping your text into html tags.
(it doesn’t do it in the comments though I’ve noticed… wierd - maybe use the comment code in place of the posting code…?)
I use MarkDown (the plugin is installed with wordpress) to format my posts.
Interesting…. I will investigate this “markdown”.
Thank you for your insight.
blockbuster video
…
http://sky.geocities.jp/usaballline/blockbuster-video blockbuster video
…
vending machine
…
http://uswebstore.freepimphost.com/vending-machine vending machine
…
oxazepam
…
http://heartland.geocities.jp/usa_sort/oxazepam oxazepam
…
investment
…
http://usatop.freehostpage.com/investment investment
…
neon light
…
http://www.geocities.jp/goodlightshere/neon-light neon light
…
tetracycline
…
http://bestusa9.freepimphost.com/tetracycline tetracycline
…
airline flights
…
http://travel2.hostxd.com/airline-flights airline flights
…
discount airline tickets
…
http://traveleasy1.i-was-in-paris.com/discountairlinetickets discount airline tickets
…