I'm currently writing some Java CORBA code and last week I spent a couple of days getting the environment set up and doing some TDD with JUnit. I wrote a few hand coded mocks because I'd already spent a little while getting everything moving and I wanted to actually deliver some working code. The first thing that I did this week was to get JMock installed and to see how it worked in comparison to my hand coded mocks. I decided to continue writing new code using TDD and the first thing that I needed to mock was an interface. This went pretty smoothly and allowing the tool to generate the mock for me saved me somewhere between 10 and 30 minutes I expect. Once the first test was passing I got to the real reason that I had wanted to try out a dynamic mocking system; I needed to mock a concrete class.
The code I'm working on is a client to some CORBA objects and as such it works in terms of object proxys that are created from IDL specifications by the CORBA IDL compiler. Whilst there are interfaces in use here the interface that you end up with for your object extends several standard CORBA interfaces that are fairly "chunky"; there's a lot of functionality to mock by hand and most of it we're not really interested in. My original install of JMock couldn't mock these objects; it required the installation of the CGLib extension jar and a version of CGLib and then a version of ASM. Once the extras were installed I switched to using the code generation JMock test base class and was successfully able to mock my CORBA objects.
By this point I was pretty happy. Even allowing for the time it took me to track down the dependency on ASM that wasn't mentioned that clearly, I was still ahead on time compared to trying to mock even one of the CORBA objects by hand. Once I'd mocked my objects I then mocked a CORBA ORB, ran my test and implemented the required functionality. Without JMock I would probably have spent an hour or so mocking up the CORBA objects and then designed my way around having to mock the ORB; I only needed a single function from the ORB,
object_to_string(), and I expect I would have created a very specialist interface with just that method, mocked that and eventually created a thin wrapper that implemented the interface and passed the call on to a real CORBA ORB. Whilst I like the fact that by being able to directly mock the complicated ORB object I didn't need to introduce the wrapper class I'm in two minds about whether the design is better or worse for passing the actual ORB into my new object. I like to restrict what code can do, so I like narrow interfaces. If I pass an interface into something and the something only uses a fraction of the interface then I'm often tempted to create a more specialised interface for the something in question to use. I find that this clearly communicates what the something is expecting to be able to do with the object that it's given. Being given a wide interface means that during maintenance it's often very easy to make the wrong decision and use one of the methods on the interface that the original designer never really intended for you to use; using interfaces that are limited to what you actually should be doing protects you from that... In this instance the only service we want from the ORB is its ability to convert an object reference to a string, we definitely do not want the class to be able to jiggle around with the more complex settings of the ORB, all of which are accessible though the same interface that the nice safe object conversion operation is available from. So at this point I'm happy with the speed gain from being able to automatically mock but a little less happy with the fact that automatically mocking may be leading to slightly less than ideal designs...
Whilst getting to this point I'd been getting to grips with the whole idea of setting expectations for my mocks. As I've mentioned before, my hand coded C++ mocks use simple text logs to record their interactions with other objects. Earlier in the year I had a long discussion with Roy Osherove about mocks that use logs verses mocks with expectations and in the end I started to see the light. Expectations are a very powerful feature and they're something that I've been adding to some of my own C++ mocks. The problem with simple logs is that you're not always interested in the same things. One usage of the mock may require that you log all the parameters to be sure that the interaction is correct whereas another may simply require that you log the fact that the call happened, you don't care what was passed. As your mocks are used they get more complex to handle the various different situations. Mocks with expectations are more complex from the start but if you're using JMock you don't need to care about that complexity because the objects are created for you. Of course I could write a generic C++ expectation solution and have all of my hand crafted C++ mocks use it but, well, I have work to do... I find the setting of expectations somewhat messy, but that's probably just because I'm used to the streamline nature of checking interaction using logs. Most of my mocks can be checked with a single line of code whereas setting up a series of complex interaction can take several rather noisy lines. I'm sure I'll get used to it.
The next issue was how to set up an expectation for a method that called back into the object under test. One of the mocks that I was using was an "object store" and it had a
load() method that tool an interface to a sink. When
load() was called the store needed to iterate over its objects and pass each one to the supplied sink interface. Thankfully this was easy to achieve in JMock by simply creating a custom
Stub object that knew what to do. This stub was slightly more complex that a hand crafted mock would have been, mainly due to the standard boilerplate that you had to provide and the fact that the stub has a single method gets called for any method signature on the object that's being mocked and this single method is generic which means you need to extract the arguments you need yourself... It's not hard, it's well documented but it is a little more code than you'd need to write if you were writing your own mock. Of course it's only a little more code if that's the only method that your hand crafted mock needs to implement. In a real world scenario the mock will have several methods that do not need any custom stub implementation at all, it's only the special cases that need to be hand coded and the fact that the hand coding is slightly more complex is made up for the fact that you don't need to hand code everything.
Unfortunately JMock fell at the final fences.
I'm quite comfortable writing unit tests for code that interacts with multiple threads. JMock doesn't play well with multi-threaded tests. The JMock documentation contains a rather pithy excuse for not dealing with threading; "The root of the problem is trying to use mock objects for integration testing." This is, quite frankly, bullshit. There are plenty of situations where you may need to test an object using multiple-threads and simply saying that "those kinds of tests aren't unit tests" is simply ignoring the real world uses of the tool. Lets take, for example, an object that acts as a producer of things. Our unit test will be acting as the consumer and, as is quite common with this patter, the producer may block if there are no things available for the consumer to consume. As soon as you have a test where the object under test may block, for any reason, you need to be able to spin up another thread so that you can continue to control the object during the test. To suggest that this isn't unit testing is somewhat naive.
One of JMock's problems with threading is that when a call occurs on a mock object the expectations are checked and if the call isn't expected the mock throws an exception that is caught by the test case that you're in. The exception fails the test. The problem is that if the mock is being accessed on another thread then the exception simply terminates that thread and the test knows nothing about the failure. I've dealt with this kind of issue before, most notably when testing COM objects in various different apartments. One trick is to provide a way for the user to create threads that can participate in the test and which can handle these exceptions and report them to the thread which is running the test. That approach would deal with my immediate problem where I want to create a second test thread but fails to deal with testing inherently multi-threaded code, such as the threaded version of my timer queue.. To solve the threading issue in all situations involves tracking the thread on which you're created and then only throwing an exception if you're on that thread. If you're not then you need to make a note of the failure and throw the exception the next time you're accessed from the creation thread. Due to Java's object lifetime rules you may need to add a call to some form of 'validation' function to the end of your test to make sure that the mock checks for other thread calls before the test ends. So, it's not an especially hard problem and I expect that if I end up using JMock a lot, and I can't find someone who already has a solution, I'll solve it and make the code available.
Another problem I came across was mocking a serializable object. The mock isn't serializable even if the object that you're mocking is. I can understand why this might not work but again it's something that's probably reasonably easy to fix. As it was I simply wrote a hand coded mock for this situation.
My 3 days with JMock were a success. The tool is very useful and very powerful and works well. Before the end of day 1 I'd already replaced the hand coded mocks that I put together last week with JMock mocks. By the end of the week the only hand coded mocks in use were ones to get around JMock limitations on serialization and threading. Once the code was complete I did a short presentation on JUnit and JMock to the rest of the team and they're all keen to start using these new tools and some of them were even quite interested in this Test Driven Development thing...