How to maximize velocity with TDD when coding a WebWork/Spring/Hibernate application
In this post I’d like to talk about the process that I find works best in terms of speed and quality of code that is adherent to good practices (the term best practices is too much sometimes, isn’t it?) when developing on a Java project that’s using WebWork, Spring and Hibernate. Among many other things, of course, but let’s consider these three to be the core ones. The overall code layout is divided into three main layers, one of them being the webwork Actions, with the second being a bunch of Services that mediate between the Actions and the third layer which, you guessed right, is the Model part which includes Hibernate repositories and other domain centric code.
The discussion intents to describe the process of a developer - preferably a pair of developers - in front a computer, writing code. Also, the IDE in this case is going to be IntelliJ IDEA, although Eclipse can - in my knowledge - do almost all the things I will be talking about. The goal is to write code fast and have an understanding of the problem at hand that gets gradually clearer and an implementation that becomes better throughout the course of development. Certainly not ruby script/generate scaffold fast, but pretty fast by Java standards.
Let’s imagine we are working on an inventory app for Gibson (that’d be the day…) and the task at hand is to create a page in a web application which will allow the user to add a guitar item to the inventory and also view a list of the guitars that have already been persisted in a table right under the form which will take the user input. I do like to keep my WebWork Actions stupid and dead simple, the main goal being every Action to have only one concrete responsibility to worry about. In this case, this could translate to, say, an AddGuitarAction and a ListGuitarsAction, even though conceptually both are captured on the same web page. This keeps things nice and simple and it’s quite useful when it comes to other people reading the code, or when functionality needs to be extended. For the purpose of this post, I will only talk about writing the ‘Add…’ bit of the task.
On with the coding then, the first class to create is AddGuitarActionTest, a unit test corresponding to AddGuitarAction which doesn’t exist yet.
public class AddGuitarActionTest extends MockObjectTestCase {
public void testShouldAddGuitar() {
AddGuitarAction action = new AddGuitarAction();
}
}
At this point, IntelliJ will complain about not finding AddGuitarAction. [alt+enter] and ‘create class’. Remember to have the class implement com.opensymphony.xwork.Action and [alt+enter] again to implement the Action’s execute() method. The Action is going to have to tell a service to add the new entry to the list of guitars. Since we have Spring to do all the IoC funky stuff for us, we can mock the interface of GuitarService in our test and set expectations without the Action really caring about what the service is going to be doing behind the scenes in order to persist the new guitar.
public class AddGuitarActionTest extends MockObjectTestCase {
public void testShouldAddGuitar() {
final String model = "SG Standard";
Mock guitarServiceMock = mock(GuitarService.class);
guitarServiceMock.expects(once()).
method("addGuitar").with(eq(model));
AddGuitarAction action = new AddGuitarAction();
action.setGuitarService(
(GuitarService) guitarServiceMock.proxy());
action.execute();
}
}
The AddGuitarActionClass doesn’t have a setGuitarService() method yet, so [alt+enter] and “create setter”. We now have a proper failing test. Create the GuitarService interface with one method
addGuitar(String model);
It would be enough at this point to implement the Action’s execute() method as:
public String execute() {
guitarService.addGuitar(model);
return SUCCESS;
}
The model field in the Action is missing now, so the same [alt+enter] drill to create it applies here. [alt+insert] to add a setter for model. The only thing remaning for the test to pass is adding the line
action.setModel(model);
And that’s about it as far the Action is concerned.
A similar process can be applied to the implementation of the the GuitarService interface and then the Hibernate guitar repository, but this is turning out a much more epic post than I was expecting it to be, so I’m going to close it just about here, as I suspect everyone gets the picture.
The great advantage of such an approach, apart from stealth, is the loose coupling and the ability to test all the parts of the task in isolation, concentrating on their responsibilities, without them having to worry about what the code they’re being injected with does, only how they interact with it. Another great thing is how the IDE ends up writing most of the code for you, lazy is always great. And of course there’s about a million other keyboard shortcuts that would have been used in the process described, you already know them, and if you don’t yet, learn them, it makes a world of difference.
As a last touch to the recipe, ping-ponging the keyboard between the dev who writes the failing test and the dev who makes it pass adds an extra bit of fun and fun is highly understated as a major factor to code quality.

June 8th, 2006 at 9:44 pm
Hi!
I am really wondering what exactly is your test doing? Check if the service method is invoked? Wouldn’t be more important to check if the model has the right values, or the model values were correctly validated, or things like this?
Even if I am co-founder of TestNG testing framework, I am much inclined to see the benefits of functional/integration testing over the unit testing (and TestNG supports these). Please let me know if I am mis-reading it ;-).
best regards,
./alex
–
:Architect of InfoQ.com:
.w( the_mindstorm )p.
June 9th, 2006 at 12:20 am
Alex,
First, I’d like to make clear that I’m not saying unit tests are by any means a replacement for functional/integration testing. On the contrary, unit, functional and acceptance tests compliment and reinforce one another. But the later two were beyong the scope of what I was trying to describe here.
However, I believe if one is to achieve a high percentage of test coverage in an application, it is very important that tests are self contained, only testing the business logic the class under test is responsible for. In which case, if the behavior of any of the related entities changes at any point, this won’t affect the results of a test that should still be passing. For instance, the action in our example would still be doing the right thing even if the GuitarService changed its behavior to append some serial number, or something, to the ‘model’ string before persisting it.
In this over-simplistic example, the action is only receiving one request parametter and calls the appropriate method on a service and that’s what should be tested in my opinion, because this is the only thing the action does. The next step would be to test drive the implementation of the service interface in the exact same manner and when it’s time to implement the model, then there should be tests that are making sure that validation is performed the way it should be and that values are persisted correctly. I find it very helpful letting a test describe to me exactly what the class I will be writing should be doing before I start writing the class. Separation of concerns is one of the Golden rules of design in my book and I think this approach is a nice way to achieve that.
Anyway, my main intention was to describe a way of making the most out of your IDE and the nice features offered by Spring and Webwork, in terms of how they can really facilitate an approach like this, which in turn can speed things up significantly and also enforce what I consider to be good, extensible design.