I have a feeling the title of this post will probably have people thinking I’m going to bash on the concept of them and that’s not really the case but at the same time I think their usefulness tends to be limited to a relatively narrow set of cases.
Where I Have Used Them
Not in as many places as I thought I would. The main area I’m using mock objects (and by the way, I’m using EasyMock 2.0) is in my code that tests some filters that are based on Spring’s OncePerRequestFilter. It makes it easier to test the code when I mock the HttpServletRequest, HttpServletResponse, ServletContext and FilterConfig interfaces. I provide the data that the Spring filter requires and provides during its interaction with these interfaces based on the particular test details. For example, I’ll tell the ServletRequest mock to expect a call to getServletPath and then give it the response as well as setting the run once attribute setting. I’ll also set some items on the FilterConfig that the filter will need. The filter also needs to set an attribute on the request as well.
The reason that mocks come in handy here is that I don’t have to create dummy implementations of these larger interfaces just for testing purposes. Also, the interaction with these interfaces I mock is pretty simple. So basically, they’re great when you have code that interacts with large interfaces but where the interaction is generally limited. More complex interactions would make the mocks, and hence the tests, much more brittle.
Mocks in general work pretty well as long as you’re using interfaces. Easy mock has some features for mocking concrete classes but I haven’t used that feature so I can’t comment on it. They also have some strong limitations, IMHO, that I think should be aware of before going too far with them.
Why Don’t I Use Them More?
There are a number of reasons, but the first that always gets me is I don’t always know how code is going to call the interface if some of my code relies on code I didn’t write or maintain. It means I have a lot of iterations through test cycles before I know how to fully script the mock. Sure I could read through the code but that’s an even bigger pain than the approach I take (at least to me it is). Take my prior example for instance. I had to give the mock to the filter and see where it failed first. If the first call it makes is to the FilterConfig then that will fail. Then it gets past that and I see it fails when it asks for an attribute in the request. And this continues on until I get it right. But that part that gets me is that I’m trying to test my filter, which in this case is a redirect filter, but I’m really doing a lot of work just to get the code I rely to get to the point where I can test my redirect filter’s capabilities. What happens if they make subtle changes in their code but the external functionality no longer changes. My tests break without functionality actually breaking because the use of the interfaces that are mocked no longer respond as they should.
To be fair, if I had created dummy implementations of those interfaces they might not fare a whole lot better since methods I don’t think are going to be called will probably cause the test to fail later on as well. Maybe for common interfaces such as the ones I use above it would be handy to have an implementation that I could easily instantiate that is built for testing interaction with the interface. In general, using code that you didn’t write and then having to test your code on top of that code, will probably be a pain no matter what.
Another reason I don’t use mocks as much as I originally expected to is because in general most of my services are built with testability in mind so I don’t need to mock up a lot of interfaces to test them. I can feed them data and they just work with the test data. Mocks tend to work best in cases where I have to deal with external systems more than anything and there aren’t suitable classes available that take the necessary test data for the code to function the way I need it to for my tests. Overall, I’d really rather have my unit tests run through real code where possible vs. mocks because then I know the interaction works where as a mock could provide a disconnect with the real system in some cases.
I suppose I’m lucky that with my personal projects I have the luxury of building code for testability in the first place so I don’t have cases where I have no choice but to mock up a more complicated interface. In my work projects most of my code deals with code generation and persistence so mocks really aren’t all that necessary for my work. How are you using mocks? Are there scenarios I don’t have to deal with that you’ve come across and where they’ve really simplified your testing life?