Java Unit Testing: Avoid the Need for Debuggers

Java Unit Testing: Avoid the Need for Debuggers

Problem 

You don’t want to have to debug your code. 

Solution 

Use unit testing to validate each class as you develop it. 

Explained 

Stopping to use a debugger is time-consuming; it’s better to test beforehand. The methodology of unit testing has been around for a long time but has been overshadowed by newer methodologies. Unit testing is a tried and true means of getting your code tested in small blocks. Typically, in an OO language like Java, unit testing is applied to individual classes, in contrast to “black box” testing where the entire application is tested. 

I have long been an advocate of this very basic testing methodology. Indeed, developers of the software methodology known as Extreme Programming (XP for short; see http://www.extremeprogramming.org) advocate writing the unit tests before you write the code, and they also advocate running your tests almost every time you compile. This group of extremists has some very well-known leaders, including Gamma and Beck of Design Patterns fame. I definitely go along with their advocacy of unit testing. 

Indeed, many of my classes come with a “built-in” unit test. Classes that are not main programs in their own right often include a main method that just tests out the functionality of the class. Here is an example:


/** A simple class used to demonstrate unit testing. */
public class Person {
 protected String fullName;
 protected String firstName, lastName;
/** Construct a Person using his/her first+last names. */
 public Person(String firstName, String lastName) {
 this.firstName = firstName;
 this.lastName = lastName;
 }
 /** Get the person's full name */
 public String getFullName( ) {
 if (fullName != null)
 return fullName;
 return firstName + " " + lastName;
 }
 /** Simple test program. */
 public static void main(String[] argv) {
 Person p = new Person("Ian", "Darwin");
 String f = p.getFullName( );
 if (!f.equals("Ian Darwin"))
 throw new IllegalStateException("Name concatenation broken");
 System.out.println("Fullname " + f + " looks good");
 }
}

What surprised me is that, before encountering XP, I used to think I did this often, but an actual inspection of two projects indicated that only about a third of my classes had test cases, either internally or externally. Clearly what is needed is a uniform methodology. That is provided by JUnit.

JUnit is a Java-centric methodology for providing test cases. You can freely download JUnit from the obvious web site, http://www.junit.org. JUnit is a very simple but useful testing tool. It is easy to use—you just write a test class that has a series of methods whose names begin with test. JUnit uses introspection (see Chapter 25) to find all these methods, and then it runs them for you! Extensions to JUnit handle tasks as diverse as load testing and testing Enterprise JavaBeans (EJBs); the JUnit web site provides links to these extensions.

How do you get started using JUnit? All that’s necessary is to write a test. Here I have excerpted the test from my Person class and placed it into a class PersonTest. Note the obvious naming pattern.

import junit.framework.*;
/** A simple test case for Person */
public class PersonTest extends TestCase {
 /** JUnit test classes require this constructor */
 public PersonTest(String name) {
 super(name);
 }
 public void testNameConcat( ) {
 Person p = new Person("Ian", "Darwin");
String f = p.getFullName( );
 assertEquals(f, "Ian Darwin");
 }
}

To run it, I need only compile the test and invoke the test harness junit:

daroad.darwinsys.com$ javac PersonTest.java
daroad.darwinsys.com$ java junit.textui.TestRunner PersonTest
.
Time: 0.188
OK (1 tests)
daroad.darwinsys.com$
The use of a full class name is a bit tedious, so I have a script named jtest that
invokes it; I just say jtest Person and it runs the previous command for me.
#!/bin/sh
exec java junit.textui.TestRunner ${1}Test
In fact, even that is tedious, so I usually have a regress target in my Ant scripts. There
is a junit task in Ant’s “Optional Tasks” package.* Using it is easy:
<target name="regress" depends="build">
 <junit>
 <test name="PersonTest" />
 </junit>
</target>

See Also 

If you prefer flashier GUI output, several JUnit variants will run the tests with a GUI. 

JUnit offers classes for building comprehensive test suites and comes with considerable documentation of its own; download the program from the web site listed earlier. 

Also, for testing graphical components, I have developed a simple component tester, described in Recipe 13.2.

0 comments:

Post a Comment