Guicing Up Your Testing, Page 5
When writing this article, as I first hit this problem it blocked me for a while. It certainly made what was a nice clean demo into something a little less clean. As I thought about it, though, I realized that this problem had value in its own right. It drives home a very important point: Although dependency injection is a fantastic tool on many levels for improving code style and design, it is not a silver bullet. This problem of setting the customer for an invoice deserves a good solution, and that solution will come from a different angle than dependency injection.
It will also come in a later article, because I think you will agree this one is quite long enough already. You have also set out to do what you wanted to do as well, which is to isolate the unit testing of the Invoice class from the slow, database reliant, DbTaxRateManager.
So, hang tight and you will come back and fix the style issues in the next article, as well as starting to look at some of the other applications of Guice beyond testing.
The Proof is in the Pudding
So, after all this work, did you get a result? Well, take a look:
Running the original test, with a simulated 5 second turnaround for database queries and updates, you see the following result:
org.bldc.guicetestingdemo.InvoiceTest passed getTotalWithTax passed . 15.018 seconds
After using Guice to inject your fake implementation based on a HashMap, you see a more reasonable:
org.bldc.guicetestingdemo.InvoiceTest passed getTotalWithTax passed . 0.154 seconds
That's a couple of orders of magnitude improvement! Okay, this is a contrived example, but real world experience tells me this is not so far off. Eliminating dependencies on file I/O, databases, remote services, and so forth can cause a dramatic increase in the speed of unit testing. This means you can run more tests in less time, and that you are testing your class implementation and not the rest of a less-than-reliable stack (of course, remember that you can also simulate a less than reliable stack as well, forcing exceptions to be thrown that might occur in the real implementation—another advantage to faking or mocking your objects).
In this article, you have explored ways to use Guice to eliminate dependencies on slow implementations when unit testing an object and replace those slow implementations with faster fake ones. Although this is a good example usage of Guice, it is far from being a one trick pony. In future articles, I hope to explore what Guice means to a much larger architectural field, leading up to a particular interest of mine, that of splitting up large systems into smaller subsystems that can be built in total isolation from one another but still be wired up to run together when necessary.
This example is also intended for only one purpose: to show what Guice can do and how it does it. The implementation of Invoice, and of the fake tax rate manager, are both rather substandard because I wanted to concentrate on just demonstrating Guice and not get bogged down in other implementation details. In the next article, I will come back and clean up this example from just a simple speed focused improvement into something a lot better in terms of style and design.
About the Author
Dick Wall is a software engineer at Google, based in Mountain View. He also co-hosts the Java Posse podcast—a regular Java-centric news and interviews show that can be found at http://javaposse.com.
Special thanks to "Crazy" Bob Lee and Kevin Bourrillion for checking this article.