20 August 2009

JMock vs. Mockito


I heard about Mockito, a new-to-me framework for writing unit tests with Mock objects, and decided to take it for a spin. Below I have shown the same test written with JMock (version 1) and Mockito (version 1.8).

Now, I know that JMock version 1 is old but the real tests for the real code have to run in Java 1.4 so that's the version we tend to use.

Here is the situation:

* I have a list of "alerts"
* Each alert is a simple form of a canned message. The object contains an email address, a message name and some parameters that are used to fill in the blank spots in the message.
* Each parameter is just a name-value pair.
* The message may have some of the names encode in it's text and, for each recipient, his own parameter values are filled in.
* I have to validate the alerts in several ways. The class to do one of those validations is the class under test.
* The validation process is to look through the list of alerts and throw out any that aren't valid. The reason they are invalid is coded into the particular validator class's code.
* Validations can be chained so this validator can have another validator injected that will be called to do further validations when this one is done.
* The validator that is being tested checks whether the exact same alert was previously sent to the same person. (It compares the parameters, message name and recipient to see about that.) It does this by looking for a matching alert in a database containing recently sent alerts.
* If it finds that the alert is a duplicate of one recently sent, it updates the date in the database to indicate an attempt was made today to send it again.
* Some other code fills the database after the alerts get sent successfully.
* Some other, other code cleans out old records from the database to give definition to the term "recent."

So, we are testing the AlertDuplicateRemover so here is one test to see what happens when there is one alert in the list and it is a duplicate.

JMock first:


public final void testStoreSentAlertsDuplicated()
{
int alertCount = 1;
AlertResultBO alertFeed = AlertBOMother.getAlertWithValidParms(alertCount);

Mock duplicatesDaoMock = mock(AlertDuplicatesDao.class);
for (int i = 0; i < alertCount; ++i)
{
duplicatesDaoMock.expects(once()).method("get").with(eq(alertFeed.getAlerts()[i])).will(
returnValue(alertFeed.getAlerts()[i]));
duplicatesDaoMock.expects(once()).method("update").with(eq(alertFeed.getAlerts()[i]),
isA(Calendar.class));
}
AlertDuplicatesDao duplicatesDao = (AlertDuplicatesDao)duplicatesDaoMock.proxy();

Mock validatorMock = mock(AlertValidator.class);
validatorMock.expects(once()).method("validate").with(isA(AlertResultBO.class)).will(
returnValue(alertFeed));
AlertValidator validator = (AlertValidator)validatorMock.proxy();

AlertDuplicateRemoverImpl remover = new AlertDuplicateRemoverImpl();
remover.setAlertValidator(validator);
remover.setAlertDuplicatesDao(duplicatesDao);

remover.validate(alertFeed, CalendarMother.yesterday());
}


And then Mockito


public final void testStoreSentAlertsDuplicated()
{
int alertCount = 1;
AlertResultBO alertFeed = AlertBOMother.getAlertWithValidParms(alertCount);

AlertDuplicatesDao duplicatesDao = mock(AlertDuplicatesDao.class);
when(duplicatesDao.get(alertFeed.getAlerts()[0])).thenReturn(alertFeed.getAlerts()[0]);

AlertValidator validator = mock(AlertValidator.class);
when(validator.validate(any(AlertResultBO.class))).thenReturn(alertFeed);

AlertDuplicateRemoverImpl remover = new AlertDuplicateRemoverImpl();
remover.setAlertValidator(validator);
remover.setAlertDuplicatesDao(duplicatesDao);

remover.validate(alertFeed, CalendarMother.yesterday());

verify(validator, times(1)).validate((AlertResultBO)any(AlertResultBO.class));
verify(duplicatesDao, times(alertCount)).get((AlertBO)any(AlertBO.class));
verify(duplicatesDao, times(alertCount)).update(any(AlertBO.class), any(Calendar.class));
}


Notes on the code:

AlertBOMother - I got the idea for this from Derek Lane a few years back. Its a class used only for testing. It has static methods that return prepopulated objects so that multiple tests can just ask for one. Its particularly useful to clean up all that code where you fill in an object's properties. Using the "Mother" hides that away so it doesn't clutter the test (and hide what its testing). It also makes the prefilled object available for multiple tests without using RBCAP (reuse by cut and paste). When properties for the object change or are added, using a 'mother' makes it easy to adjust the tests. Finally, it makes it easy to start your testing with a simple version of the object and then improve the contents used for testing later.

Both mock the DAO that is used to read (get) and update the database records. 'get' is used to see if the alert is already there (implying it was already sent). A null is returned if no match is found in the database.

Both also mock the other validator to which this validator chains after working its magic.

The mocks, in both cases, are injected into the class-under-test as they would by by Spring in real life. That class knows nothing about Spring so there is no need to call any of Springs special methods to do Spring stuff.

Mockito code seems a little simpler. There is a bit less clutter to hide the intent of the test.

I never did like how JMock has you put a method name in a string. Mockito doesn't do this.

JMock sets up the expectations ahead of time. Mockito checks the expectations after the fact. That makes Mockito more like the way JUnit works. In both, you do stuff and then see if the results are right.

JMock sort of mixes up the expectation code and the setup code. Let me explain. There is code to tell the mock object what to do. That's what I call 'setup code'. Three is code to check that the expectations of what happened during the running of the code really happened. That's the 'expectation code.' Notice that in JMock, both types of code preceed the test and are mixed together. In Mockito the setup preceeds the test and the expectations follow it.

Interestingly enough, running inside Eclipse 3.3 under Java 6, the Mockito tests took twice as long to run as the JMock tests. This is the time for all the tests not just the one test shown above. The difference was only 1/2 second in this case but it could add up. I'm not sure how much it matters as I find that only about 10-15% of my tests need mocks anyway. (But, maybe I need to write more tests.)

Neither framework has very good documentation. In both cases, it consists of a few pages of examples and some javadoc. (I should note that I'm talking about JMock 1 here. The docs for JMock 2 may be better.) JMock is here http://www.jmock.org/index.html and Mockito is here http://mockito.org/

Finally, I don't pretend to be an expert in either framework. I've been using JMock on and off for 4 years on several projects. This is my first attempt with Mockito.

I'm sure there are better ways to use both and one or the other may shine when the tests are more complicated. (On the other hand, complicated tests are a pain in the ... neck and I really like simple ones better.)