You need to validate parameters passed to a method. You would prefer one line of code to test that a method parameter is valid.
Use Validate
to check method
parameters. Validate
can check for
empty or null
parameters, as well as
evaluate any logical conditions. Example
1-18 demonstrates the use of Validate
to perform a few simple
validations.
Example 1-18. Using Validate to perform simple validations
import org.apache.commons.lang.Validate; public doSomething( int param1, Object[] param2, Collection param3 ) { Validate.isTrue( param1 > 0, "param must be greater than zero" ); Validate.notEmpty( param2, "param2 must not be empty" ); Validate.notEmpty( param3, "param3 must not be empty" ); Validate.noNullElements( param3, "param3 cannot contain null elements" ); // do something complex and interesting }
Write finicky methods. Don't just work with any input, have some
standards. Don't hang around with garbage or null
s—clean up your act, and use Validate
. Methods in Validate
throw an IllegalArgumentException
if invalid parameters
are encountered. Validate.notEmpty( )
will throw an exception if the parameter is null
or empty, and Validate.isTrue( )
takes a logical expression
that must evaluate to true. Table
1-5 lists a number of static methods of Validate
.
Table 1-5. Available static methods on Validate
Validate method |
Description |
---|---|
|
Fails if expression evaluates to false |
|
Same as above; constructs exception with a message |
|
Fails if collection contains a null |
|
Fails if array contains a null |
|
Fails if collection is empty |
|
Fails if map is empty |
|
Fails if array is empty |
|
Fails if object is null |
Well-written code tests input and parameters. Is the number within
a given range? Has the user supplied an index that is out of bounds for
this array? Unless you want your applications to break down and throw
exceptions, make sure that you are not trying to access the 15th element
of a 10-element array; your code should be skeptical of parameters and
refuse to run if it doesn't get its way. In fragile systems, parameter
validation is not a top priority; errors are dealt with as different
application layers throw a potpourri of exceptions. Other, more
unstable, systems will pass a null
all the way down the call stack until the lowest level blows up and
spits back a NullPointerException
—with a long-winded stack
trace. Don't play around with null
—avoid it, test your system exhaustively,
and throw an IllegalArgumentException
.
Consider the following method, which takes a latitude, longitude,
and mode parameter. Both the latitude and longitude must fall within a
valid range, and this method should throw an IllegalArgumentException
if it encounters an
unrealistic latitude, an unrealistic longitude, or an empty mode
parameter. The Validate
utility used
in conjunction with a DoubleRange
object can reduce the amount of code dedicated to range checking and
method parameter validation. To test the validity of the coordinates,
two constant DoubleRange
objects are
created outside of the method. DoubleRange.includesDouble(double
d)
returns true if the number falls within the
defined range—if x <= rangeMax && x
>= rangeMin
. (See Example
1-19.)
Example 1-19. Using the Validator and the DoubleRange to validate method parameters
public static final DoubleRange LAT_RANGE = new DoubleRange( -90.0, 90.0 ); public static final DoubleRange LON_RANGE = new DoubleRange( -180.0, 180.0 ); public double elevation( double latitude, double longitude, String mode ) { Validate.notEmpty( mode, "Mode cannot be empty or null" ); Validate.isTrue( LAT_RANGE.includesDouble( latitude ), "Lat not in range " + latRange, latitude ); Validate.isTrue( LON_RANGE.includesDouble( longitude ), "Lon not in range " + lonRange, longitude ); double elevation = 0.0; // code to implement the elevation method return elevation; }
It takes a fair amount of institutional discipline to make sure
that a code base has a sufficient amount of validation, and it is
surprising how often systems scale to thousands of lines without it.
Systems without good internal validation and sanity checks are
characterized by frequent occurrences of NullPointerException
and RuntimeException
. Throwing RuntimeException
in the form of IllegalArgumentException
might seem like an
odd recipe for increasing overall stability. But, if you actively throw
exceptions and test exhaustively, it will be less toil and trouble to
identify and fix defects—your application will fail sooner, and you will
have short stack traces and well-defined problems. Performing rigorous
parameter validation alone will not create a stable system, but it is
part of a larger equation, which involves unit testing and
validation.
In addition to parameter validation, you should be writing unit
tests that test this validation. If you write good unit tests using
JUnit and then measure your test coverage with a tool like Clover or
JCoverage, you can save yourself an amazing amount of grief fixing
unanticipated defects. Your unit tests should be covering close to 100%
of your code, and they should throw curveballs. Don't just test the
expected situation—if your method takes a double, see what it does with
Double.POSITIVE_INFINITY
; and if your
method takes an array, pass it a null
. Test the unexpected; if you find that
your method melts down with a null
,
check the parameter with Validate
.
100% test coverage is not an unrealistic ideal; it will drive your
validation code. More test coverage always leads to a higher level of
system quality.
Chapter 8 discusses DoubleRange
and other utilities for capturing
a range of numbers.
Chapters 4 to Chapter 7 of the Java Extreme Programming
Cookbook by Eric Burke and Brian Coyner (O'Reilly) are great
introductions to unit testing tools and concepts. Read this book for an
introduction to JUnit, HttpUnit, mock objects, and the idea of unit
testing. Think of Validate
as a
runtime test for a method; the methods on Validate
are analogous to the assert( )
methods in JUnit's TestCase
class.
If you are interested in measuring code coverage of unit tests, take some time to look at Clover from Cortex eBusiness. Cortex eBusiness offers a free license to Open Source projects, but if you are using the product in a commercial environment, it is around $250. More information about Clover is available at http://www.thecortex.net/clover/. Other than Clover, there is another tool named JCoverage, which can also be used to measure test coverage. More information about JCoverage is available at http://www.jcoverage.com/. For more information about JUnit, go to http://www.junit.org/.