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.

Tuesday, November 06, 2007

EclipseWorld

One of our partner companies, Artifact Software, will be showcasing their latest version of Lighthouse tomorrow at EclipseWorld.

Lighthouse is a single application which delivers complete traceability for software projects. Trace requirements status in real time, view history of project changes instantly, get complete project health reports in seconds and much more. Who knows, maybe they will mention us too :)

We use Lighthouse ourselves to manage our requirements, bugs, test cases, project plans, and issues. As a matter of fact, we are considering opening up access to our users so they can share their opinions of our upcoming development efforts. Interested in sharing your opinion? Let us know.

Labels: ,

Thursday, September 27, 2007

See You at the Verify Conference in DC

We will be hanging around at the Verify Conference October 29th and 30th. Let us know if you are attending! Come by and check out our latest product, hear our latest announcements, and walk away with some great swag!

Labels:

Monday, September 10, 2007

Taking Exception with Exceptions

Exceptions in Java are thrown when an abnormal condition arises. Exceptions not handled by a method are passed (via the throws keyword) to its parent. More often that not, test suites focus on the normal behavior of a method, and skip over the cases where the unhandled exceptions are thrown. A comprehensive unit testing effort should test each error condition separately. Thus, fewer thrown exceptions means fewer individual tests will be needed.

Consider a method with this signature:

public void foo() throws Exception1, Exception2, Exception3

What if there's a bug in this method that causes Exception1 to be thrown instead of Exception2? In order to fully test this method, our test suite needs to include at least three JUnit tests to confirm that the proper exception is thrown when the corresponding abnormal condition arises.

Even if the correct type of exception is thrown, it may be the case that all three exception types are handled the same way, or it may be that some of the exceptions can be handled by the method itself. If we can remove even one of the thrown exception types, we'll reduce our testing effort by one-third. There is no shortcut for reducing the number of thrown exceptions, of course -- indeed, each distinct exception may be necessary -- but by keeping in mind the number of thrown exceptions as you code, you may be able to streamline your testing effort down the road.

Labels: , , ,

Sunday, September 09, 2007

Lose Those Extra Parameters

Most unit testing strategies focus on a method's parameters. By definition, they control the behavior of the method under test, so varying the input values of the method, one can build a strong test suite. But the more parameters that your method under test has, the more tests that will be required to do complete black-box testing. Obviously, most methods take one or more parameters, but if there are clear ways to limit the number of parameters, it will be a clear win for black-box testing scenarios.

Consider this method signature:

public int addOrSubtract(int x, int y, Operator operator)

Each additional parameter increases the number of possible combinations that you'd likely want to test. If you try three possible values for each parameter, that's 27 tests for this method. If there are only two possible operators (addition and subtraction), we could refactor this method into two simpler ones:

public int add(int x, int y)
public int subtract(int x, int y)


Now, even if we try three possible values for each parameter, we still only wind up with nine combinations for each method, or 18 total -- we've reduced our testing effort by one-third. Obviously, there is no cut-and-dried method for reducing the number of parameters, and there may indeed be cases where your method under test does require many parameters, but by keeping the method's signature to as few parameters as possible, you'll save yourself testing effort down the road.

Labels: , ,

Monday, August 27, 2007

eWeek features Codign Software

Darryl Taft of eWeek wrote an opinion piece on three local companies ... Codign Software, Artifact Software and Avicode.

I know the companies well. Currently, I am a co-founder of Codign, the Products Director at Artifact, and a friend to many at Avicode.

Anyway, read up on us if you get a chance! There is certainly a lot of software expertise around the Chesapeake Bay, and we in Baltimore like to contribute as well!

Labels: , , ,

Monday, July 23, 2007

Seven Metrics to Improve Your Unit Testing

Unit testing is tough, regardless of manual or automated, test-first or test-after. With any of the approaches, there are certain metrics to pay attention to while you code. Staying within acceptable levels means your code will be much more testable, whether you unit test or not!

  • Parameters - Each parameter added to a method's signature increases the number of possible ways that method can be invoked, eventually creating an unmanageable combinatorial problem. By limiting the number of parameters to three or fewer, you can easily increase the testability of your code.
  • Exceptions - Each thrown exception represents a distinct error condition that must be tested, and error conditions are very hard to test. Keeping the number of thrown exceptions to three or less greatly simplifies your testing effort.
  • Cyclomatic Complexity - Cyclomatic Complexity (CC) is a measure of the decision logic in a method. The more decisions your method has, the more ways it will behave. Keeping the number of CC paths to 10 or less greatly reduces the risk of defects as well as increases the overall testability of your code.
  • Lines of Code - One of the oldest and simplest metrics used to measure a method's testability. A shorter method is easier to understand and test.
  • Paths - This metric looks at distinct data paths. Data paths are paths that link a data element's definition to its use. In other words, if you define a data element, you should use it, and if you use it, you should test it. Data paths often overlap with Cyclomatic Complexity paths.
  • Static Invocations - Methods under test invoke methods from external classes, and these external classes can usually be mocked or dummied up to behave exactly as required for an individual unit test. Static invocations of external methods often have an unchangeable behavior that thwarts even the best testing effort. Avoid this unnecessary coupling to external classes, and keep your external static invocations to a minimum.
  • Anonymous Classes - Anonymous classes make unit testing difficult because they exist only within the method under test, and cannot themselves be unit tested. Although anonymous classes are convenient (especially for GUI programming), their lack of testability and mockability can hinder your testing effort.

Labels: , ,