Tuesday, August 14, 2007

Java Mock Object Frameworks Reviewed

I've been reading the book "xUnit Test Patterns: Refactoring Test Code" so you are going to get a few posts on unit testing. The book is huge, 27 chapters and 944 pages, and packed with useful information. This clearly was no hastily compiled book, the author has invested a lot time and effort. The book has a website here. Right now I'm reading the chapter on "Test Doubles", what you and I would probably call mock objects, but the author classifies into five types: Dummy Object, Test Stub, Test Spy, Mock Object, and Fake Object. The classification is sensible and really makes you think about how you use mocks and stubs. I've been using mock objects for years and never really thought that much about it.

Despite using mock objects for years, I haven't kept up with the the mock objects frameworks. Until recently I've been using the original static mockobjects.com libraries and had not tried any of the dynamic mock frameworks like DynaMock, EasyMock, or jMock. I've finally gotten tried of writing custom mock objects and decided ti was time to try something new. Over the last couple weeks I've been testing EasyMock, jMock, and rMock. So here are my thoughts on the those frameworks.

EasyMock 1 (Java 1.3 or 1.4)

EasyMock uses recording to set expectations. A mock instance is created and the expected method calls are specified by method calls with the expected parameters.
  • Documentation is decent and better than other v1 kits.
  • Includes a tutorial with source code.
  • Generally seems simpler than the others to understand.
  • Some extra code, instances of the control and mock instance for each mock.
  • Need calls to the replay and verify methods for each mock used in a test.
  • Recording expectations using actual method calls is an easy to understand metaphor.
  • Specifying return values is not so easy to understand. I thought it clashed with the recording model. The inconsistencies with the model of recording of expectations makes things harder.
  • Had to record method calls, using null values, even when I did not care about what parameters were used to invoke a method. It seems like this would make it harder for someone else to understand the intent of the test.
  • Less proxy casting than jMock, but two variables are needed for each mock. Need the mock and a a reference to the interface that the mock implements.
  • Tests extend standard JUnit TestCase.
jMock 1 (Java 1.3 or 1.4)

jMock uses expectation specification and essentially implements it's own little language for setting expectations on a mock.
  • Documentation is ok, but could be better. There are lots of classes so it can be hard to figure out where to look for something in the Java Doc. The tutorial is not included in the download. There is no full example with source code.
  • jMock Usage is very consistent across all aspects of setting expectations and return values.
  • Expectation definition at first seems verbose, but needs fewer lines of code than EasyMock. Expectations and return values are specified together which can make them easier to read and understand.
  • Method names are specified as strings and may hamper refactoring.
  • Casting proxies is annoying.
  • Tests must extend MockObjectTestCase.
rMock 2 (Java 1.3 and up)

rMock follows the same model of recording expectations as EasyMock.
  • Quite a bit of documentation, but it was not as useful. Had to generate my own Java Doc. No source code examples are included.
  • This framework seems to want to totally redefine how unit tests are written. It implements a whole new assert framework which I found confusing, but it does work with JUnit.
  • No advantage over EasyMock.
  • No special support for JUnit 4 features or Java 5+.
  • Tests must extend RMockTestCase.
EasyMock 2 (Java 5 and up)

This is a nice upgrade from version 1.
  • Documentation the same quality as version 1. Includes a tutorial with source code.
  • Much improved over version 1.
  • Less code than version 1, no control objects, no proxy casting.
  • Does require static imports for the cleanest looking code. More static imports than jMock.
  • Still requires calls to replay and verify methods for every mock in a test.
  • Still relatively the simplest to understand.
  • Same inconsistencies in the model between recording expectations and setting return values as the previous version.
  • Tests extend standard JUnit TestCase.
jMock 2 (Java 5 and up)

This is a significant upgrade that I found much easier to use.
  • Documentation is better. The tutorial is not included in the download. Still no full source code examples.
  • Less code, no proxy casting.
  • Does require static imports for the cleanest looking code.
  • Model is more consistent and simpler.
  • The syntax of expectation setup needs a little getting used to.
  • All mock variables must be declared final.
  • Seems to have the best features of record and play back without the inconsistencies.
  • Tests no longer have to extend MockObjectTestCase. Extending MockObjectTestCase is probably still the easiest thing for JUnit 3.
While I prefered JMock, EasyMock is also an excellent framework. Either jMock or EasyMocl would be a good choice. I do not recommend rMock. The choice between EasyMock or jMock will come down to personal preference and perhaps the skills of your developers. I think the slightly steeper learning curve of jMock is worth it for the consistency of its model. I was pleasantly surprised at how much easier jMock 2 was to use over jMock 1.

3 comments:

Nat Pryce said...

In jMock 2 you do not have to extend MockObjectTestCase to use mock objects. The MockObjectTestCase is provided as a convenience when using JUnit 3, but you don't have to use it. A different convenience API is provided for JUnit 4.

Richard Hansen said...

Thanks Nat. I've updated the comments.

Anonymous said...

i still don't get mock frameworks. I might be missing something. when i am unit testing i normally don't care how my collaborators bahave. in fact, i want them to behave exactly as i tell them. That's why i only like the returns and not the method call expectations. can someone shed some light? thanks.