Five Unit Test Tips: #2: Change your code to make testing easier
It’s been a while since I promised to write top five unit testing tips, so I guess I should better start writing #2. As with my first example this one is based on a real-life story.
Your code is worth nothing if it can’t be tested. Change your code to make it more testable.
In my last project, we used a pipes and filter architecture, with messages being passed forward through a chain of services. Most services refined the messages or broke them up in smaller services and passed them on to the next service. When they refined the message, they often stored new messages to the data layer as a side effect. To test the results, we read the generated objects back from the data layer. I had code like this:
Before
@Test
public void fileShouldCreateTransactionObject() {
Reader simulatedFile = new StringReader(createFileContents());
FileRequest file = new FileRequest(filename, simulatedFile);
TransactionRepository repo = new TestTransactionRepository();
FileService fileService = new FileService();
TransactionService transService = new TransactionService();
fileService.setNextStep(transService);
fileService.setTransactionRepo(repo);
fileService.process(simulatedFile);
Collection transactions = repo.findAll();
assertEquals(1, transactions.size());
Transaction transaction = transactions.iterator().next(); // Bleh!
// Here is the real code I wanted
assertEquals("foo", transaction.getTransactionType()); // Whatever
}
The solution was to change the design away from the pipes-and-filters design. Instead, the process()
method returns the newly created Transaction
object:
After
@Test
public void fileShouldCreateRequestObject() {
Reader simulatedFile = new StringReader(createFileContents());
FileRequest file = new FileRequest(filename, simulatedFile);
FileService fileService = new FileService();
Transaction transaction = fileService.transform(simulatedFile);
// Here is the real code I wanted
assertEquals("foo", transaction.getTransactionType()); // Whatever
}
This new design totally violated our architecture, but it’s clearer, it keeps the infrastructure away from the code, and it’s much less brittle. Our test transaction repository used to be vulnerable to being polluted with test data from a previous run. As we no longer use this sort of infrastructure objects, we don’t have to worry about interaction between tests.
If you don’t know how to test your architecture, the problem is not with the testing, but with the architecture.
Until next time: Happy testing.
Comments:
[Example Citizen] - Oct 8, 2008
This entry seems to have been mostly copied without attribution at http://techmemo.org/2008/10/03/change-your-code….
jhannes - Oct 11, 2008
Weird. Thanks for pointing this out. The site seems to be replaced with a parked site now.
Johannes Brodwall - Oct 11, 2008
Weird. Thanks for pointing this out. The site seems to be replaced with a parked site now.