Guidelines for Java Testable Design
By: Lasse Koskela
This article is based on Unit Testing in Java, to be published in April 2012. It is being reproduced here by permission from Manning Publications. Manning early access books and ebooks are sold exclusively through Manning. Visit the book’s page for more information.
Issues with testability boil down to our inability to write tests or the excess trouble we have to go through to get it done. These troubles include:
* Inability to instantiate a class.
* Inability to invoke a method.
* Inability to observe a method’s outcome or side-effects.
* Inability to substitute a test double.
* Inability to override a method.
Having faced off with these testability issues again and again over the years, we have come to appreciate a few simple heuristics—guidelines—for testable design. Let’s take a look at them. The following is a set of dos and don’ts I have gathered and whose importance I’ve learned through repeated yak-shaving. They aren’t in any particular order and none of them are universal truths—just something to keep in mind so that you think twice before deciding to go against these guidelines.
Avoid complex private methods
There’s no easy way to test a private method. Therefore, we should strive to make it so that we don’t feel the need to test our private methods directly. Note that I didn’t say you shouldn’t test your private methods but that you shouldn’t test them directly. As long as those methods are trivial utilities and shorthands to make your public methods read well, it should be perfectly fine that they get tested only through those public methods.
When the private method isn’t all that straightforward and when we do feel like we want to write tests for it, we should refactor our code so that the logic encapsulated by the private method gets moved over to another object responsible for that logic—and where it is a public method as it should be.
Avoid final methods
Very few programs need final methods. The odds are that you don’t need them either.
The main purpose of marking a method as final is to ensure that it isn’t overwritten by a subclass. In fact, Oracle’s Secure Coding Guidelines for the Java Programming Language suggests that “making a class final prevents a malicious subclass from adding finalizers, cloning and overriding random methods.” While that is true in some context, it doesn’t mean that you should need the final modifier.
There are two problems with the above logic. First, those potential subclasses are likely written by the people sitting next to you. Second, the Reflection API can be used to remove the final modifier. In practice, the only situation where you might reasonably want to make a method final is when 1) you load foreign classes at runtime or 2) you don’t trust your colleagues (which sounds like you have much bigger issues to worry about).
What about the performance of final?
One of the arguments that people sometimes lean on in support of final methods is performance. Namely, it is said that since final methods cannot be overridden, the compiler can optimize the code by inlining the method’s implementation.
It turns out that the compiler cannot safely do this because the method might have a non-final declaration at runtime. However, a JIT compiler would be able to inline such methods at runtime, which does present a theoretical performance benefit.
With that said, Jack Shirazi and Kirk Pepperdine, the authors of Java Performance Tuning, Second Edition, and javaperformancetuning.com, have said, “I wouldn’t go out of my way to declare a method or class final purely for performance reasons. Only after you’ve definitely identified a performance problem is this even worth considering.”
... to read more articles, visit http://sqa.fyicenter.com/art/