Putting Your Code to a Test

For something that's so simple its creators say that you can "write your own equivalent," the JUnit testing framework has nonetheless become an indispensable part of the development process for many Java coders.

For something that’s so simple its creators say that you can “write your own equivalent,” the JUnit testing framework has nonetheless become an indispensable part of the development process for many Java coders.

JUnit is a Java class library that supports unit testing, or testing that compares the actual result of a discrete method with an expected result. If the actual result and the expected result match, the test passes; otherwise, it fails.

The idea behind unit testing is that you should create one or more unit tests for a class method when — or even better, before — the method is written. Each unit test codifies correctness, and a class isn’t released until it passes 100 percent of its unit tests. Later, when you return to the code and make changes for a new release, a failure in a unit test indicates a flaw or a regression (where previously working code now behaves differently and incorrectly) in some of the newly-introduced code.

Unit testing provides concrete evidence that a snippet of code is functioning properly. Used consistently, unit testing lets developers introduce new features into working code or refactor existing code with confidence. In some sense, using JUnit for “test-driven development” is like doing pair programming with a partner that has a photographic memory of every weird use case that’s been overcome during the course of a project. Not only does this impossibly perfect collaborator remember all past obstacles, he thumps you on the head the moment you introduce code that breaks any pattern of correct usage. As the unit testing mantra goes, “If the [results match and the] light’s green, the code is clean.”

Created by Kent Beck and Erich Gamma, based on Beck’s original work in Smalltalk, JUnit is just one of more than forty implementations of unit testing frameworks. Frameworks exist for most programming languages, including C, C++, Python, and Ruby. (The XProgramming web site offers a full list at http://www.xprogramming.com/software.htm.) JUnit is released under the open source, IBM Common Public license, and is available on the web at http://www.junit.org. This column was prepared with version 3.8.1.

Testing, 1, 2, 3…

JUnit is a framework to design, build, and run unit tests for Java code. Let’s jump in and see how it works.

Listing One contains a simple JUnit unit test that validates Java 2′s java.utilStringTokenizer class.

Listing One: SimpleTest.java, a simple, but canonical JUnit unit test

01 package org.cadenhead.workbench;
03 import java.util.*;
04 import junit.framework.*;
06 public class SimpleTest extends TestCase {
07 String inputString;
09 protected void setUp() {
10 inputString = “7, 420, 1862″;
11 }
13 public static Test suite() {
14 return new TestSuite(SimpleTest.class);
15 }
17 public void testTokenizer() {
18 StringTokenizer tokens = new
StringTokenizer(inputString, “,”);
19 assertEquals(“7″, tokens.nextToken());
20 assertEquals(” 420″, tokens.nextToken());
21 assertEquals(” 1862″, tokens.nextToken());
22 }
23 }

The root class of a unit test is TestCase, found in junit. framework. To create a unit test, you simply create a subclass of TestCase (line 6), and code one or more methods, where each method represents one or more test cases. Each of the testing methods should begin with the word test… and have no arguments (as in line 17). JUnit uses introspection to automatically find and run each test…() method.

Each test method consists of three parts: statements that establish the preconditions required to test the method, statements that execute the method, and statements that determine whether the test passed or failed.

Here’s an example unit test (that’s similar in intent to TestCase in Listing One, but simpler to discuss, at least for now.)

01 public void testTokenizer() {
02 // establish the preconditions
03 String inputString = “7, 420, 1862″;
05 // exercise the object
06 StringTokenizer tokens = new
StringTokenizer(inputString, “,”);
08 // test the postconditions
09 assertEquals(“7″, tokens.nextToken());
10 assertEquals(” 420″, tokens.nextToken());
11 assertEquals(” 1862″, tokens.nextToken());
12 }

Code that sets up example objects and primitive data used by the method are called the method’s fixture. In the code above, lines 3-6 are the fixture for testTokenizer(). Line 3 initializes a string, and line 6 creates the actual object. Lines 9-11 verify that the string was tokenized properly. As you can see from the code, the expected result is the three tokens 7, 420, and 1862. If any one of lines 9-11 fail, the entire unit test fails.

Setup and Teardown

While each test method typically has its own fixture, the entire unit test (a set of test methods) may require or benefit from a fixture, too. A fixture for an entire unit test might create objects shared by several test methods. (Those objects can be instance variables of the test class.)

The class method setUp() is called automatically at the beginning of a unit test to set up objects for the entire test case. setup() can be overridden. The class method tearDown() is called after each test to dispose of objects and other resources, and can also be overridden.

For example, the precondition in the testTokenizer() method in the code shown above could be moved into setUp()(as it is in Listing One):

public class SimpleTest extends TestCase {
String inputString;

protected void setUp() {
inputString = “7, 420, 1862″;

// rest of the class …

By the way, if the unit test class is part of the same package being tested, the unit test’s methods can access methods that are private to the package.

Pass or Fail

Once you have your fixtures written, it’s time to write the actual test cases. To compare actual results against expected results, use the class methods of Assert, found in junit. framework.

The class method assertTrue() fails when its boolean argument is false. assertFalse() does the opposite. Both methods are very easy to use.

assertTrue(header.windowTop > -1);
assertFalse(header.windowBottom < 0);

The class method assertEquals() fails when the two arguments passed to it are not equal. It also displays the string representation of both objects to show where they differ. There are versions of this method for objects and for each primitive data type. Here’s an example:

assertEquals(3, Math.min(5, 3));

The class method assertNotNull() fails if its object argument is null. assertNull() does the opposite:


The class method assertSame() fails when the two arguments passed to it are not the same object, and assertNotSame() does the opposite:

String suffix1 = “.opml”;
String suffix2 = “.opml”;
assertSame(suffix2, suffix1);
assertSame(“.opml”, suffix1);

There are versions of each assert…() method with an additional argument, a string that becomes part of the error message of an AssertionFailedError exception. If you use these methods, place the message argument before any other arguments, as in this example:

String input1 = “4″;
Vector output1 = new Vector();

output1.add(new Integer(4));
assertEquals(“One state”, output1, testHeader1.convertStateHeader(input1));

If a class is to be used in unit tests, it often requires the implementation of equals() and toString() methods. The equals() method defines what it means for two objects to be equal. In the Object class, two objects are equal only if they are the same object.

The toString() method provides information on the contents of an object, which is used by assertEquals() to demonstrate how an object differs from the expected value.

In some situations, it may be necessary to declaratively fail a test by calling the fail() method, as in this example:

public void testConvertDateHeader() {
// set up preconditions
String input = “Sun, 01 Jun 2003 13:18:20 GMT”;
SimpleDateFormat sdf = new SimpleDateFormat (“yyyy-MM-dd ‘at’ HH:mm:ss z”);

// exercise the method
Date output = null;
try {
output = sdf.parse(“2003-06-01 at 13:18:20 GMT”);
} catch (ParseException pe) {

// test the postconditions
assertEquals(output, testHeader1.convertDateHeader(input));

In this example, the catch block should never execute because the parse() method of the SimpleDateFormat is called correctly. However, on the chance this might change due to problems with the classes that set up the preconditions, failing the test indicates that something is amiss.

From Test to Suite

Once you have a set of unit tests, you can run them as a sequence by adding them to a test suite, represented by the TestSuite class in junit.framework. Each TestCase object has a suite() class method where each test can be manually added to the suite by calling the object’s addTest() method with the test’s TestCase object. However, JUnit 3.8.1 supports an easier way to add all tests to a suite: Simply create a TestSuite object with the test class as an argument, like so:

public static Test suite() {
return new TestSuite(SimpleTest.class);

Given a set of unit tests collected in a test suite, you can run the suite with one of the three JUnit test runners, junit. textui.TestRunner (a command line tool), junit. swingui.TestRunner (a Swing-based tool), and junit. awtui.TestRunner (an AWT-based tool).

Putting Java to the Test

Listing Two and Listing Three test a more complex class, OpmlHeader, a part of a class library that reads outlines formatted in the XML dialect Outline Processor Markup Language, or OPML (described in detail at http://www.opml.org). OpmlHeader makes use of XOM, an open-source XML library (covered in the March 2003 issue of Linux Magazine, available online at http://www.linux-mag.com/2003-03/java_xom_01.html).

Listing Two: OpmlHeader.java reads OPML headers

package org.cadenhead.workbench;

import java.text.*;
import java.util.*;

public class OpmlHeader {
String title;
Date dateCreated;
Date dateModified;
String ownerName;
String ownerEmail;
Vector expansionState;
Vector vertScrollState;
Integer windowTop;
Integer windowLeft;
Integer windowBottom;
Integer windowRight;

public OpmlHeader() {

protected Vector convertStateHeader(String header) {
Vector states = new Vector();
StringTokenizer tokens = new StringTokenizer(header, “,”);
while (tokens.hasMoreTokens()) {
try {
states.add(new Integer(tokens.nextToken().
} catch (NumberFormatException nfe) {
// ignore
return states;

protected Integer convertPositionHeader(String header) {
Integer position;
try {
position = new Integer(header);
} catch (NumberFormatException nfe) {
position = null;
return position;

protected Date convertDateHeader(String header) {
Date event;
SimpleDateFormat sdf = new SimpleDateFormat(“EEE, dd MMM yyyy
HH:mm:ss zzz”);
try {
event = sdf.parse(header);
} catch (java.text.ParseException pe) {
event = null;
return event;

public String getTitle() { return title; }

public Date getDateCreated() { return dateCreated; }
public Date getDateModified() { return dateModified; }
public String getOwnerName() { return ownerName; }
public String getOwnerEmail() { return ownerEmail; }
public Vector getExpansionState() { return expansionState; }
public Vector getVertScrollState() { return vertScrollState; }
public Integer getWindowTop() { return windowTop; }
public Integer getWindowLeft() { return windowLeft; }
public Integer getWindowBottom() { return windowBottom; }
public Integer getWindowRight() { return windowRight; }

public void setTitle(String inTitle)
{ title = inTitle; }

public void setDateCreated(String inDate)
{ dateCreated = convertDateHeader(inDate); }
public void setDateModified(String inDate)
{ dateModified = convertDateHeader(inDate); }

public void setOwnerName(String inOwnerName)
{ ownerName = inOwnerName; }

public void setOwnerEmail(String inOwnerEmail)
{ ownerEmail = inOwnerEmail; }

public void setExpansionState(String inExpansionState) {
expansionState = convertStateHeader(inExpansionState);

public void setVertScrollState(String inVertScrollState)
{ vertScrollState = convertStateHeader(inVertScrollState); }

public void setWindowTop(String inWindowTop)
{ windowTop = convertPositionHeader(inWindowTop); }

public void setWindowLeft(String inWindowLeft)
{ windowLeft = convertPositionHeader(inWindowLeft); }

public void setWindowBottom(String inWindowBottom) {
windowBottom = convertPositionHeader(inWindowBottom);

public void setWindowRight(String inWindowRight)
{ windowRight = convertPositionHeader(inWindowRight); }

public String toString() {
return “OPML headers: \n”
+ “Title: ” + title + “\n”
+ “Date Created: ” + dateCreated + “\n”
+ “Date Modified: ” + dateModified + “\n”
+ “Owner Name: ” + ownerName + “\n”
+ “Owner E-mail: ” + ownerEmail + “\n”
+ “Expansion State: ” + expansionState + “\n”
+ “Vertical Scroll State: ” + vertScrollState + “\n”
+ “Window Top: ” + windowTop + “\n”
+ “Window Left: ” + windowLeft + “\n”
+ “Window Bottom: ” + windowBottom + “\n”
+ “Window Right: ” + windowRight + “\n”;

The OpmlHeader class in Listing Two reads the header portion of an OPML file and saves its contents in instance variables. Here’s an example of the XML it reads:

<dateCreated>Sun, 01 Jun 2003 13:18:20 GMT</dateCreated>
<dateModified>Thu, 26 Jun 2003 22:52:40 GMT</dateModified>
<ownerName>Rogers Cadenhead</ownerName>
<expansionState>5, 6</expansionState>

To make this information more convenient to work with in Java, several elements, such as dateCreated and dateModified. are converted from strings into other forms. The value of these elements are fed to the convertDateHeader() method of the class, which creates a Date object from a string in RFC-822 format.

Listing Three contains OpmlOutlineTest. java, a JUnit test case class that verifies the data-conversion methods of OpmlHeader. (Trying out the code also requires OpmlOutline.java, a class that’s available from the magazine’s web site.)

Listing Three: OpmlOutlineTest.java, a JUnit test suite

package org.cadenhead.workbench;

import org.cadenhead.workbench.*;
import java.text.*;
import java.util.*;
import junit.framework.*;

public class OpmlOutlineTest extends TestCase {
OpmlHeader testHeader1 = new OpmlHeader();

public static Test suite() {
return new TestSuite(OpmlOutlineTest.class);

public void testConvertStateHeader() {
String input1 = “4″;
Vector output1 = new Vector();
output1.add(new Integer(4));
assertEquals(“One state”,
output1, testHeader1.convertStateHeader
String input2 = “4, 13, 18″;
Vector output2 = new Vector();
output2.add(new Integer(4));
output2.add(new Integer(13));
output2.add(new Integer(18));
assertEquals(“Multiple states”,
output2, testHeader1.convertStateHeader
String input3 = “”;
Vector output3 = new Vector();
assertEquals(“No states”,
output3, testHeader1.convertStateHeader

public void testConvertPositionHeader() {
String input = “50″;
Integer output = new Integer(50);

public void testConvertDateHeader() {
String input = “Sun, 01 Jun 2003 13:18:20 GMT”;
SimpleDateFormat sdf = new SimpleDateFormat(
“yyyy-MM-dd ‘at’ HH:mm:ss z”);
Date output = null;
try {
output = sdf.parse(“2003-06-01 at 13:18:20 GMT”);
} catch (ParseException pe) {
assertEquals(output, testHeader1.

Write On!

If you’re working with many other developers, or are refactoring your own code, or just trying to squash bugs and avoid regressions, JUnit is simply invaluable. A good unit test is the embodiment of a requirement, however small.

Though it’s easy to write unit tests, writing good unit tests can be a challenge. So, here are a few helpful suggestions:

* As the methodology’s name implies, test one discrete unit at a time.

* Test close to the metal. Don’t test a class via its subclass — if there’s an error, you won’t be sure which class is the problem.

* Give test methods long, descriptive names. When taking code out of mothballs months — or even years — after its last release, a test method name such as testUndeclaredXmlEntities() reflects a specific situation that was encountered and resolved during development. (It also provides example input that originally caused the problem.)

A phrase that’s used to describe programming with JUnit and other unit testing frameworks is, “Test a little, code a little.” JUnit founders Kent Beck and Eric Gamma explain, “In general, your development will go much smoother if you write tests a little at a time as you develop. It is at the moment that you are coding that you’re imagining how that code will work. That’s the perfect time to capture your thoughts in a test. When you are developing in this style you will often have a thought and turn immediately to writing a test, rather than going straight to the code.”

It’s an unusual mindset for those unfamiliar with unit testing, but beginning with a test case can make it easier to develop new features and debug old ones:

* If you write a test before you add a new feature, when the test passes, the feature is complete.

* If you respond to a bug report by writing a test that should work but currently doesn’t, then when the test passes, the bug’s been squashed.

* If you refactor or change existing code and the modified code passes the test suite (the light is green), the code is clean.

Test these techniques out for yourself.

Next month, Java Matters ogles Naked Objects, an innovative new framework for using and learning Java by interacting directly with Java objects.

Rogers Cadenhead is a Web application developer and the coauthor of Teach Yourself Java 2 in 21 Days, Third Edition from Sams Publishing. Visit his weblog at http://www.cadenhead.org/workbench. The code for this month’s column can be found at http://www.linux-mag.com/downloads/2003-09/java.

Comments are closed.