« HTTPUnit is NOT! | Main | Servlets are for integration »

Don't mock infrastructure

Listen to this articleListen to this article

While there are a few cases where I feel you may need to, for the most part I feel very strongly that mocking infrastructure is a fundamentally flawed approach to testing.

I have been down the path of mocking JNDI, JDBC, JMS, UserTransaction, etc. and whilst at the time I thought this was all very cool, I did wonder what the hell I was doing writing all this stuff.

Eventually I realised that the problem I was having was one of abstraction. Or more properly a lack thereof. I was tackling the wrong problem. Instead of thinking my class needs to lookup JNDI therefore I need to mock out JNDI, I started to think about the problem in general. That is, a mechanism for looking up services.

A perfect example is Using mock naming contexts for testing. While I think the intention is great, I don't believe it goes far enough.

JNDIs Context has a multitude of methods I'll never use, not to mention the fact that they all throw NamingException which quite frankly my code has never known how to deal with and usually throws a ImGivingUpException.

What I really need is a ServiceLookup interface with a few very simple methods like, not surprisingly, lookup(Class). Then I can have two implementations, one a MockServiceLookup that I can create inside my JUnit test and return whatever I like and a JndiServiceLookup that actually understands how to go about performing all the nasty JNDI stuff.

Not convinced? Ok well now imagine I have more than one way to obtain a service. Sometimes it's through JNDI and sometimes I instantiate a local object. If all my code is tied to a JndiLookup what will I do? Instead, now that my code is tied to an interface, I can create an implementation that say first looks in a local configuration and then if that fails, delegates to the JNDI version. Or maybe a SuperServiceLookup that holds a list of other ServiceLookup implementations and just delegates to them as appropriate. Again referencing only the interface and not any concrete implementation.

Now think JMS. Instead of writing to/reading from queues, why not have MessageQueue interface with two simple methods: read(Message) write(Message). then I can have a MockMessageQueue that I instantiate in my test and a JmsMessageQueue that talks to the real thing.

Again, we've abstracted the problem. That is, sending/receving messages. Not talking to JMS.

So now, back to the original example, I can see that a MockInitialJndiContext may well be useful but probably only for one thing: testing my JndiServiceLookup. And then I can probably just have a constructor in JndiServiceLookup that accepts an Context and a default constructor that creates an InitialContext. Then use something like Easy Mock to fill in the rest for me.

When it comes down to it, InitialContext is really a convenience for calling InitialContextFactory anyway which in turn creates a Context which is after all an interface. So why be constrained by someone elses API?

My rule of thumb is that in general, I want to be mocking out my own interfaces not someone elses. I'll usually have many places where I need to mock out a given interface of my own and at most one place where I need to mock out someone elses.

Using Factory and AbstractFactory, etc. for creation will then allow you to use Strategy, Decorator, etc. for different behaviour ie. Mock vs JNDI, JMS, etc. Martin Fowlers Patterns of Enterprise Application Architecture also has some great ways for doing this for enterprise applications as well.

Using testability to drive my thinking forces me to abstract problems in a way I hadn't previously and good design emerges. This is why I'm such a huge TDD bigot. Because as Dan North point out, TDD is not about testing.

TrackBack

Listed below are links to weblogs that reference Don't mock infrastructure:

» MockObjects from Confluence: Gonzo
What are Mock Objects?MockObjects|http://www.mockobjects.com is a testdriven development process first presented|http://www.connextra.com/aboutUs/mockobjects.pdf at XP2000. Starting pointThe Mock objects wki|http://www.mockobjects.... [Read More]

Comments

Take a look at an IoC container like Spring Framework, Picocontainer, XWork's IoC, or Hivemind.

In this way, you can abstract your service lookup even more, since you don't DO a lookup. The container provides your dependencies to your objects at object creation and you just configure your container to get the services from wherever they exist (Spring has particularly good support for both local services and EJBs).

I completely agree. There's just something that doesn't feel right about having to mock up huge APIs in order to test your code. If the problem is writing strongly encapsulated, highly testable code then mocking seems really to be a partial problem at best. I agree that the problem is ultimately one of abstraction. One thing I'd recommend is to adopt the IoC pattern. Instead of making core business objects lookup resources have them set or passed through the constructor. Now the only question is how to abstract JDBC and Servlets.

Thanks for the comments guys and yes you are quite correct. IoC containers are a great way of implementing this. I deliberately stayed away from even mentioning IoC or AOP or anything like that because I felt it detracted from the core message which was TDD and abstraction.

As for abstracting JDBC, that's pretty easy: Hibernate, DAOs, Castor, JDO, you name it, someone else has gone to the trouble of abstracting the problem of persistence for me. Usually I layer my own abstraction on top of that to make it easier to test and again so that I can code to my own API.

Servlets? Well again I'd say don't put stuff into servlets in the first place. Use XWorks, or Spring, or Struts/Scaffolds/Tiles, or roll your own but don't code directly into servlets.

Nicely put!

Mock objects are used for iterative, top-down design in a TDD process. That is, you start from the object you want to write and mock the interactions you want that object to make with other objects in its environment. You then define those interactions as interfaces that are implemented by objects that you design using mocks, and so on until growing your application as a web of collaborating objects.

Eventually you will be able to implement an out-going interface of one of your objects as a thin veneer over an existing API. But the object using that API will do so through an expressive interface that is defined in terms of your application, not some generic (often unpleasant) Java API.

You make some interesting points and another bunch of classes that I'm currently writing tests for are Struts-like actions. Without mocking out the request and response, what other options are there when testing?

When you say struts-like, I assume you aren't using struts. So, not knowing much about your application, how about put your own interface ontop of the request. Only allow your actions to access behaviour that they really need. Say to get/set parameters as a start. That way you can mock out a very simple API to test your actions. The Request and Response interfaces are quite large. I doubt you'll need even close to all methods in your actions. Generally I try to have my struts actions simply pull out the relevant stuff from the request and pass them to objects that have no knowledge that they're running in a web application at all. Again, I see that servlets are really an integration tier. They should pull out information from a request and convert it into a form that plain old java objects can use.

That's right, I'm using a very small MVC framework as opposed to Struts. The actions themselves are simply lightweight adapters to the functionality provided by other objects. For example, a typical action will grab some information from the request, call some other methods, put information into the request/session and return the name of the next view. Although I considered abstracting the actions away from being reliant on the services provided by the servlet spec, I always find that I end up needing some of the features provided by HttpServletRequest and HttpSession classes. For me, mocking out the servlet classes is very simple and provides me with exactly what I need to test the actions. I think I've only needed to mock out half a dozen methods related to getting/setting parameters and attributes with the remainder of the methods being no-ops. The other thing that these mocks allow me to do is test things like Filters which is useful. I was a bit skeptical about mocks at first and while I do agree with not mocking out the entire infrastructure, a few mocks here and there are certainly very useful.

Sounds good. Sounds like you've isolated the servlet specific stuff to simply transforming between HTTP and POJOs anyway. What are you using for mocking stuff out? I've found easymock to be really good.

My mocks are very, very simple and created using this process. In IntelliJ, I created a new class that implemented the required interface and then selected "Implement interface" from the menu to generate all the no-ops. I then added a HashMap, filled in the getParameter() method and a setParameter() method. I'll certainly take a look at EasyMock though - thanks for the pointer.

That's pretty much the approach I used to take. Gotta love IntelliJ. I've since grown to love EasyMock because I no longer have all these extra classes around that I continually have to update. Thanks for all the feedback. I'd like to hear what you think of the easy mock stuff.

Post a comment