Tuesday, December 04, 2007

Keeping an Eye on Static Invocations

Method invocations are what drive every piece of Java software. One method calls another and together they provide the functionality that makes up your particular program. Unfortunately, it's those subordinate methods that can cause you headaches when creating tests for your method. You need them behave in a particular way during testing so that you can force your method under test to exhibit a specific, testable behavior.

In the case where the invoked methods are instance methods of some class, it's usually possible to create mock or dummy classes that provide the test behavior that you require for your method under test. But what about the case when the invoked method is static? Consider the following sample code:

This computePrice() method simply determines the price of a given RetailItem, and doubles that price if the current day is a Saturday.

There are two subordinate methods called by computePrice(). The getBasicPrice() method is owned by the RetailItem parameter, and we can most likely pass in a MockRetailItem if we want that method to return a particular value for our tests.

The getToday() method is another story. Since it's invoked statically, there's no way to inject a MockTimeUtil into computePrice(). Unless the TimeUtil class supplies other methods that will allow us to change the current day before we invoke computePrice(), we can only test the TRUE outcome of the IF statement if we run this test on a Saturday. (And who wants to come in to work Saturday just to test their code?)

It's clear that the static invocation of getToday() will be an obstacle during testing, so we're probably better off if we can eliminate it altogether. For example, we could pass the current day in as a parameter, which would easily let us force a particular outcome during testing:

This refactoring maintains the integrity of the code and simplifies our unit testing effort. Before we get carried away with eliminating every static invocation, we should consider a couple of things.

The first thing to consider is that in eliminating our static invocation we've added a new parameter to our method. As we've discussed in a previous post, each additional parameter increases your testing effort, so in some sense we've only traded one unit testing effort for another. In this case, however, the trade-in of a static invocation for a new parameter is well worth it due to the ease with which we can mock a parameter.

The second factor to consider is that every rule has an exception, and sometimes static invocations are desirable, or even unavoidable. Consider how often static methods like Integer.parseInt(String) are used. Rewriting a method under test to avoid these common static methods might actually increase your overall testing effort.

Despite these considerations, you should keep an eye on the number of static invocations you use in your code. By maintaining as few static invocations as possible, you'll give yourself the flexibility you need to complete your testing effort.

0 Comments:

Post a Comment

<< Home