Recipe 1.14 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.

Discussion

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.[6] Using it is easy:

[6] In some versions of Ant, you may need an additional download for this to function.

<target  name="regress" depends="build">     <junit>           <test name="PersonTest" />     </junit>  </target>

See Also

If you prefer flashier GUI output, several JUnit variants (built using Swing and AWT; see Chapter 14) 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.

Remember: Test early and often!



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net