I got asked a very simple question today: how do you go about adding unit tests to an already existing J2EE app? In particular, so you can test your logic standalone, without needing to go through a deployment cycle and test it in the server?
My interlocutor made the very good point that when you develop a J2EE application test-first (as I advocate), you usually end up with a very good set of “mocks” for the various services provided by the container. However, writing these so you can test a class is a lot of effort (though it will pay itself back in time). So, was there an easier way?
This is what I replied:
One option is, of course, not to test your component in isolation. That is, instead of focusing on unit tests, look at writing integration-level tests that operate against a deployed version of your system. This typically means that your tests are slower, but usually gives benefits.
Another option is to take a very piecemeal approach. This is going to be sort of hard to explain, but here goes…
You’re writing a test for a piece of functionality, right? Move the business logic for the functionality into a method by itself, and then have other methods that use the J2EE environment to provide the data for the logic. You can then write a unit test for the business-logic method (which is typically where the errors are), plugging in sample data. At this stage, you hope that the environment stuff works (more accurately, you rely on integration level tests to show that).
Lather-rinse-and-repeat this for the entire class. You will ultimately end up with a lot of methods that provide your functionality, and another lot of methods that hook that functionality in the environment. The functionality will be fully tested through JUnit tests, and the “hooks” through integration-level tests.
Okay, now for the next step… yank all of the business functionality out into another class, leaving delegate methods back in the original class. Move your unit tests to suit. What you should now have is a single POJO class that has a lot of business logic, and a J2EE-based class that integrates the business logic back into your class. The aim here is to totally divorce your business logic from the environment.
At this point, you probably have a semi-decent abstraction of the services the environment provides for. You’ll probably even have good mocks for them. However, at no point have you taken a big overwhelming step. Of course, to get to the end goal will take a lot of time.
The ultimate trick is to stop thinking “I’m writing a J2EE application”, and start thinking “I’m writing an application that uses external services, one possible implementation of which is J2EE”.
- There was a blog post I read not long back on this point, of treating J2EE as a service. The exact example I was read was how you could create a simple lookup service, and supply a JNDI-based implementation or a mocked one, so as to avoid having to mock out the more complex JNDI interface. Unfortunately, I couldn’t hunt down where I read that, so I can’t give credit where credit is due, alas.
- If anyone else out there is stuck with a big ball of mud, this is the definitive article on unsticking it…