Recap
In my last Guice article, Guicing Up Your Testing, I explored how Guice and Dependency Injection could be used to substitute faster alternatives to slow resources in your tests to increase performance and reduce reliance on complex or flaky systems.
You took a test that relied on a tax rate lookup system that simulated the use of a slow database. By using Dependency Injection techniques, you eliminated the slow database implementation of the lookup and replaced it with a fake object that just did the lookup using a HashMap implementation.
As you left the first article, you had achieved a couple of orders of magnitude speedup (albeit from a fairly contrived example) but had also taken at least one step backwards in the implementation of your object model.
In the original form, the Invoice Object took a Customer as a constructor argument and hence could make that Customer final and immutable within the invoice. It also used a singleton to obtain the TaxRateManager object that was implemented with the kind of latency that a heavily used database connection might have.
In the Guiced up form, the singleton was eliminated in favor of a TaxRateManager that could be injected. Guice also took care of the creation of the Invoice and TaxRateManager objects as required, but as a result you lost the ability to pass the Customer in as an argument to Invoice, meaning that the field could no longer be final and immutable, but in fact had to be set once the Invoice had been created.
This was all intentional in the first article—where the focus was really on the Dependency Injection approach and how you achieved it with Guice, and I did not want to dilute the message with lots of other considerations like this. However, now is the time to go and put some of that right.
No Silver Bullet
The first thing to recognize is that there is no silver bullet in software development. Just like so many other tools that have preceded it, Guice is staggeringly useful and very neat, but it is not the answer to all problems (not even most of them). I actually like libraries that don’t try to be the total solution, but rather concentrate on doing just one thing and doing it really well. In this, Guice is very successful, being a perfect answer to Dependency Injection in Java—it’s lightweight, simple and fast.
On the other hand, even the simple testing example highlights some of the limitations. As soon as you lost control of the creation of Invoice, handing that control over to Guice, you lost some control over the immutability of the Customer in the Invoice.
It should be apparent that making Invoice something that Guice creates directly is an incorrect design decision. Instead, you can mix it with another pattern instead: Factory.
A Brief Aside on Design Patterns
The statement about no silver bullets applies, in my opinion, the same to design patterns as it does to libraries such as Guice. Design patterns can encapsulate useful concepts, but over-reliance on them can lead to trouble similar to the trouble you have seen by relying on Guice for everything. Your initial example of the Database-implemented TaxRateManager was slow and difficult to test precisely because you used the Singleton pattern to obtain that TaxRateManager (which made it hard to substitute another implementation in its place).
In the Guice implementation, you did end up using Singleton scope for the TaxRateManager so you could argue (and people do) that the Singleton design pattern is not at fault but rather the implementation is. This is a fair argument, but I still maintain that always thinking in terms of patterns boxes your own approach to development. Design patterns have their place, but so does another favorite of mine: (anti-patterns) and yet another: independent and original thinking.
That said, in this situation there is a strong case for a combination of Guice dependency injection and the Factory design pattern.
You will use Guice to create an InvoiceFactory instance, which will be injected with the TaxRateManager you configure in the Guice binding. You then can use that InvoiceFactory to create new Invoice objects, and which will pass in both the TaxRateManager and a Customer instance that you pass in. That way, both attributes can be made immutable.
Fixing the Cruft
So, you can correct the problems introduced by your previous work to include Dependency Injection. First, you will alter Invoice to take both a Customer and a TaxRateManager in the constructor arguments, and you will also remove the Guice @Inject (because Invoice will not be directly injectable):
private final Customer customer; private List<Item> lineItems; private final TaxRateManager taxRateManager; public Invoice(TaxRateManager taxRateManager, Customer customer) { this.taxRateManager = taxRateManager; this.customer = customer; // set up the line item list this.lineItems = new ArrayList<Item>(); }
Notice that you have restored the TaxRateManager field to final, and that the constructor takes both the Customer and the TaxRateManager as arguments. You have also pulled out the @Inject.
However, now Guice can’t do any injection into this class, so you need to create an InvoiceFactory that will do the work (fortunately it’s nice and short):
package org.bldc.guicetestingdemo; import com.google.inject.Inject; public class InvoiceFactory { private TaxRateManager taxRateManager; @Inject public InvoiceFactory(TaxRateManager taxRateManager) { this.taxRateManager = taxRateManager; } public Invoice createInvoiceForCustomer(Customer customer) { return new Invoice(taxRateManager, customer); } }
There should be no surprises here. Guice is tipped off to @Inject TaxRateManager into the InvoiceFactory constructor. When you are ready to create an Invoice, you invoke the createInvoiceForCustomer method and pass the customer to use in. This then creates a new Invoice using the injected TaxRateManager, and the Customer you desire.
Why use a Factory and not some other design pattern like a Repository here? For what you need right now, a Factory is ample. Your tests do not require a repository to track and look up invoices by some index. Until there is such a need, I am a big believer in keeping it simple, and it doesn’t get much simpler than that factory class.
See what that does to your test (just the important changes):
private TaxRateManager taxRateManager; private InvoiceFactory invoiceFactory; @Before public void setUp() throws Exception { .... invoiceFactory = myInjector.getInstance(InvoiceFactory.class); } .... @Test public void getTotalWithTax() { System.out.println("getTotalWithTax"); Customer customer = new Customer(CUST2_ID, "FredCo"); Invoice invoice = invoiceFactory.createInvoiceForCustomer(customer); .... }
Instead of getting an Invoice instance in the constructor, you get an instance of InvoiceFactory instead. Then, in the test, InvoiceFactory is used to create an instance of an Invoice using the Customer object. This is much cleaner than the previous implementation.
One other thing was pointed out by several reviewers of my first article and I am in total agreement. Instead of TaxRateManager taking a Customer ID, it makes more sense for it to take a Customer instance and return the tax rate based on that instance. This makes the API a little cleaner.
For this, you need to update the TaxRateManager interface, and the fake object implementation of it.
public interface TaxRateManager { public void setTaxRateForCustomer(Customer customer, BigDecimal taxRate); public void setTaxRateForCustomer(Customer customer, double taxRate); public BigDecimal getTaxRateForCustomer(Customer customer); }
And the new Implementation:
@Singleton public class FakeTaxRateManager implements TaxRateManager { .... public void setTaxRateForCustomer(Customer customer, BigDecimal taxRate) { taxRates.put(customer.getCustomerId(), taxRate); } public void setTaxRateForCustomer(Customer customer, double taxRate) { this.setTaxRateForCustomer(customer, new BigDecimal(taxRate)); } public BigDecimal getTaxRateForCustomer(Customer customer) { BigDecimal taxRate = taxRates.get(customer.getCustomerId()); if (taxRate == null) taxRate = new BigDecimal(0); return taxRate; } }
It’s pretty minor stuff, but it creates a cleaner API to use the TaxRateManager now. The Invoice getTotalWithTax method now has to pass in the Customer rather than the ID:
public BigDecimal getTotalWithTax() { BigDecimal total = this.getTotal(); BigDecimal taxRate = this.taxRateManager.getTaxRateForCustomer(customer); BigDecimal multiplier = taxRate.divide(new BigDecimal(100)); return total.multiply(multiplier.add(new BigDecimal(1))); }
AssistedInject
Mixing the factory pattern with your dependency injection has solved the style issues you introduced in the first article by using Guice, so you might be asking yourself why Guice doesn’t offer something to do this for us because this would seem to be a fairly common situation.
Well, Guice actually might offer it soon. Jesse Wilson and Jerome Mourits, a pair of engineers at Google, have created a Guice add-on called AssistedInject which formalizes the use of the factory pattern described above and makes it more Guicy as well. You can download and use the extension now, and a description of it is available on the Guice Google group. It it also going to be submitted into the core Guice project for future inclusion.
Recap
So, that’s pretty much it. You can download the source code in the form of a NetBeans project that has been adapted to use both Guice and the factory pattern. You have corrected many of the style issues introduced in the first article when you added Guice to the application. What you should take away from this is that Dependency Injection, although very useful, is only one tool in the toolbox. It can be mixed with other software development practices and design patterns where appropriate, and where it makes sense. Used correctly, it can make your implementation and architecture more beautiful. If that is not the case, you are probably mis-using Guice and you should look for another, cleaner way of achieving the same thing (like in this case—using Guice to inject into a factory class instead, and then using the factory to create instances with immutable properties).
Testing Both Sides of Your Class
Looking at what you have so far, are you doing a good job of testing your Invoice class? Probably not; one test under ideal conditions is not very exhaustive. You get some points for adding a couple of different items to the Invoice and then asking for the total with tax—forcing it to sum up the cost of the items and apply the tax to that sum, but you are only testing one possible customer so you should probably make another call with a different customer and resulting tax rate, and ensure that the total is correct for that as well.
What about customers that don’t exist? Your implementation of the FakeTaxRateManager returns a 0 for the tax rate in this case—in other words, it fails silently. In a production system, this is probably not what you want; throwing an exception is probably a better idea.
Okay, say you add a test for another customer that exists with a different tax rate, and check the total for that is different from the first customer. Then, you add another test for a non-existent customer and expect an exception to be thrown. Are you well covered then?
I would like to also make sure that these Invoice instances don’t interfere with each other, so a test to add items to different Invoices with different Customers to make sure there is no effect from adding items in one invoice to the total in the other seems like a good idea too.
All of this sounds very exhaustive—surely you have tested your little class well if we do all of this? At least, that is what you would think if you were only used to looking at the one side of your class, the API calls (or if you like, the input to your class). There is another side though, what this class calls—in particular, the calls it makes to the TaxRateManager.
You can tell that the calls are probably pretty near correct because you are getting back the amounts you are expecting, but suppose Invoice is very inefficient and calls the TaxRateManager ten times instead of one to try and get the answer? In a system that needs to be highly performant, that is unacceptable overhead. You want to make sure that, for a given call to getTotalWithTax, only one lookup call is made, for the correct customer, to the TaxRateManager.
Enter EasyMock
The answer lies in using a Mock object. There are several such libraries available for Java; a popular one is EasyMock and is the one you will look at in this article.
EasyMock is brilliant. It allows you to take an interface definition and create an instance of a mock object that conforms to the methods of that interface. It then lets you script up what you are expecting to be called on that interface, what it should return, and any exceptions it should throw. You then replay the script, run your test, and at the end you can validate that everything was called as expected and the exact number of times expected. Too many calls, or the wrong calls, or even too few calls (something expected that was never invoked) will cause an error during the validation stage. If this is not yet clear, don’t worry; there is an example below that should illustrate all this much better.
By using EasyMock to implement any objects that your class calls, you can therefore test not only the calls in to the class, but also anything it calls out to—literally testing all sides of your class.
See how you use EasyMock to create an injectable instance of a mock object. In your TaxRateTestingModule class, you can create the instance of the mock object you want to use, and then bind it to the TaxRateManager interface with Guice:
import com.google.inject.Binder; import com.google.inject.Module; import org.easymock.EasyMock; public class TaxRateTestingModule implements Module { public void configure(Binder binder) { TaxRateManager mockTaxRateManager = EasyMock.createMock(TaxRateManager.class); binder.bind(TaxRateManager.class).toInstance(mockTaxRateManager); } }
To use this, you will need to download EasyMock and add the easymock.jar to your project libraries. This will allow you to import org.easymock.EasyMock. You then can use the static method createMock from this class to create a mock object using the TaxRateManager interface. This is then bound to the TaxRateManager in Guice using the binder method “toInstance()”.
Now, see how to obtain and use the easymock instance in a test:
@Test public void getTotalWithTax() throws Exception { System.out.println("getTotalWithTax"); Customer customer = new Customer(CUST2_ID, "FredCo"); EasyMock.expect(taxRateManager.getTaxRateForCustomer(customer)) .andReturn(new BigDecimal(6.25)); EasyMock.replay(taxRateManager); Invoice invoice = invoiceFactory.createInvoiceForCustomer(customer); double expResult = 106.25; // Expected total with tax // add some line items invoice.addItem("Beach Ball", "Beach Ball in Multiple Colors", 37.55); invoice.addItem("Swimsuit", "Fashionable swimsuit, black", 62.45); BigDecimal result = invoice.getTotalWithTax(); assertEquals(expResult, result.doubleValue()); EasyMock.verify(taxRateManager); }
EasyMock has been imported again into this class, and just as before taxRateManager has been injected into the test as well (the easymock instance is injected by the TaxRateTestingModule). The easymock object conforms perfectly to the TaxRateManager interface, but when used in conjunction with static methods on EasyMock, it can do ever so much more.
The line:
EasyMock.expect(taxRateManager.getTaxRateForCustomer(customer)) .andReturn(new BigDecimal(6.25));
does what it sounds like—it actually scripts up the expected interaction for the easymock instance. In this case, it literally says “Expect the getTaxRateForCustomer method to be called with the customer instance, and when that happens return the value 6.25 as a BigDecimal.”
The next line tells EasyMock to switch from the “recording” mode where you are telling it what to expect into replay mode, where the interaction we have scripted up should be followed.
The test then calls getTotalWithTax that turns around and calls getTaxRateForCustomer on the easymock instance, which true to the script returns 6.25 as the answer. You assert that the value comes back for the total with tax. As a final step, you call a “verify()” method on the taxRateManager instance that makes sure that all of the expected calls were actually made.
This last part is the real power of EasyMock. By verifying the easymock instance, you are making sure not only that the right calls were made, but also that all expected calls were made. This way, you make sure that your Invoice class really is calling the TaxRateManager, with the right argument, and calling it exactly one time. Anything else will generate an error at some point in the test—either when too many calls are made to the instance, or when it reaches the verify and there are unmade calls that were expected.
Test this out—comment out the two lines that call getTotalWithTax() and the assert of the correct value right after it. When running, you should see the following failure:
Testcase: getTotalWithTax(org.bldc.guicetestingdemo .InvoiceTest): FAILED Expectation failure on verify: getTaxRateForCustomer(org.bldc.guicetestingdemo .Customer@749700e7): expected: 1, actual: 0 junit.framework.AssertionFailedError: Expectation failure on verify: getTaxRateForCustomer(org.bldc.guicetestingdemo .Customer@749700e7): expected: 1, actual: 0 Expectation failure on verify: getTaxRateForCustomer(org.bldc.guicetestingdemo .Customer@749700e7): expected: 1, actual: 0 at org.easymock.internal.MocksControl .verify(MocksControl.java:71) at org.easymock.EasyMock.verify(EasyMock.java:1306) at org.bldc.guicetestingdemo.InvoiceTest .getTotalWithTax(InvoiceTest.java:69)
It isn’t the prettiest error, but it does let you unwind what has gone wrong. The problem is clearly in the getTotalWithTax test case, and it clearly states the problem is with the verification stage—in particular, the getTaxRateForCustomer method, which is expecting one call with “org.bldc.guicetestingdemo.Customer@749700e7” but getting none.
That org.bldc.guicetestingdemo.Customer@749700e7 is a bit of a problem for me as well; it doesn’t tell you much of any use really, does it? Adding a toString method to your POJOs ought to be part of your standard coding practice:
@Override public String toString() { return "Customer {ID=" + customerId + ", Name=" + customerName + "}"; }
Now, when you run the test you will see the more useful message:
getTaxRateForCustomer(Customer {ID=43214321, Name=FredCo}): expected: 1, actual: 0
Now, you can see that the expected Customer instance is ID 43214321 and name FredCo. Much more useful. You now can debug the code and look for who is dropping the ball when calling TaxRateManager with these details.
I am a huge fan of EasyMock. It makes testing very easy indeed, and much more effective as well. It is not limited to just method calls and returns, but can do a lot more. For example, say you want to simulate an exception being thrown when you try and get the tax rate for a non-existent customer:
@Test public void getTotalWithTaxNoSuchCustomer() throws Exception { System.out.println("getTotalWithTaxNoSuchCustomer"); Customer customer = new Customer(11111111, "NoSuchCo"); EasyMock.expect(taxRateManager.getTaxRateForCustomer(customer)) .andThrow(new NoSuchCustomerException("NoSuchCo")); EasyMock.replay(taxRateManager); Invoice invoice = invoiceFactory.createInvoiceForCustomer(customer); invoice.addItem("Something cool", "Something really cool", 29.95); try { BigDecimal result = invoice.getTotalWithTax(); fail("Should have not been able to find the customer"); } catch(NoSuchCustomerException ex) { // expected behavior assertEquals("NoSuchCo", ex.getMessage()); } EasyMock.verify(taxRateManager); }
Here, you set up a customer with a bogus ID and name (in truth, because you are using EasyMock, you could even use a valid customer ID and name and still force an exception, but this hurts the readability of the code. Using something like “NoSuchCo” really spells it out for someone reading your test.
Of course, in the real world, you would probably want to tighten up the Invoice class so that it threw an exception if you tried to create an invoice against a non-existent customer as well. This being a simple illustrative article though, I will leave such an exercise to the reader.
Notice that you are still expecting the call to getTaxRateForCustomer with these bogus customer details, but this time you respond with a thrown NoSuchCustomerException (the “andThrow()” method on the EasyMock script). You can use the same trick to simulate all kinds of failures—for example, by using EasyMock, it becomes trivial to simulate a flaky network being used to call a distributed object—throwing the occasional RemoteException to test that your code copes okay is a great way to bulletproof your library. The same goes for simulating transactional exceptions against some class that normally accesses a database and so on.
Wrapping Up
If you download the accompanying source code project for this article and look at the EasyMock implementation, you will see the whole example fleshed out, and with an extra test not covered here as well; it demonstrates multiple expected calls to getTaxRateForCustomer, returning different values for different customer instances. This allows you to test that Invoice returns different totals depending on different customer instances and tax rates. You always can get pretty inventive with your testing and should do so. EasyMock makes it simple and quick to come up with all sorts of different scenarios, including test failures, exceptions, even simulating bad data conditions (what would your code do if TaxRateManager happened to return a negative number, or not a number, and so forth?).
When chatting with Bob Lee about this article, he uttered the statement “Guice and EasyMock could have been made for each other.” Certainly the two in combination make for an extremely compelling solution to unit testing just about any code you can think of.
Is Guice then a one trick pony? Clearly it is great for testing, but is that all it is useful for? Emphatically no! Guice was actually created with quite a different use in mind, that of breaking up dependencies in large systems, and it is that I shall talk about in the next article. From my point of view, this is a far more interesting and important use of Guice. Tests are a great way of grasping what Guice can do, but once you understand the techniques of dependency injection, you can move on to more advanced situations where it can be of use.
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.