JUnit Tutorial

Introduction

This is not a thorough treatise on JUnit. But if you haven't used JUnit before, I suspect that it is about as much as you need.

Getting JUnit

Go to Sourceforge for downloads. The download button is towards the middle of the Web page, on the righthand side. At the time of writing the current version is 3.8.1, from September 2002.

JUnit also comes bundled in with a lot of other software. In our experience, this kindness often causes trouble because of version inconsistencies or classpath problems. So we recommend that you

  • get JUnit 3.8.1 (or whatever is current) from Sourceforge
  • and then find out and remove any other versions of junit.jar on your system
While you might think it extreme, it can save you hours of confusion.

If you are using Maven, it will try to defeat you on this score, downloading its own copy of JUnit and hiding it in $HOME/.maven . Look through the Maven scripts, find out what is doing this, and comment it out.

Don't forgot to remove the jar from .maven

Installation and the Classpath

The jarfile needs to be installed where it will be on whatever classpath you are using. If you are using JUnit with Ant, there are very good reasons for putting the Ant and JUnit jars in the same place, say in $HOME/lib - a particularly good place because the name is short and easy to remember.

Once again, you need to be sure that there is only one set of Ant jars around ( ant.jar and optional.jar ) and that these are on the right classpath.

It's important to keep in mind that there are at least three relevant classpath's -- the one seen by Java when it starts running Ant; the one seen by Ant; and the classpath passed by Ant to JUnit and Quilt when they are running tests.

Ant must be on the first classpath. The second classpath is defined in build.xml . JUnit and Quilt must be on that classpath, or Ant will not be able to find them to run them. The third classpath is defined in the <quilt> element in build.xml . That must include junit.jar, and quilt.jar . It's easiest to manage these requirements if all four jars are in the same place and on all three classpaths.

Setting Up Your Work Directories

While it is by no means necessary, it makes things much easier if unit tests are set up in a parallel hierarchy to that used for the software under test. The Quilt software is set up like this:

      
  src
    java
      org
        quilt
          cl
            QuiltClassLoader.java
  src
    test
      org
        quilt
          cl
            TestQuiltClassLoader.java

    

The target directory, which the compiler writes compiled classes to, is organized in exactly the same way.

Writing New Code

If you are writing new code from scratch, the approach sketched out so far can make for very rapid software development. Each class in the java side of the source hierarchy is matched with a class on the test side. The name of the second is that of the first prefixed with "Test". Code development goes in a fast loop:

  • write Javadoc comments specifying the code
  • write some code
  • write tests verifying that the code is correct
  • run Ant
  • regenerate Javadocs periodically
  • when all tests succeed, run Quilt coverage tests to verify that testing is thorough
This cycle should be iterated several times a day, so that code always either works or is very close to working.

JUnit Tests

Most JUnit tests are written like this:

  • Create a skeleton in the right package.
  • import junit.framework.*
  • import whatever other packages you need
  • create a public class extending TestCase
  • add a constructor with a single String argument that passes the argument to super
  • optionally add a setUp
  • optionally add a tearDown
  • add a lot of public void testX(){}

This gives you:

package com.xyz.stuff;

import junit.framework.*;

public class TestStuff extends TestCase {
    public TestStuff(String name) {
        super(name);
    }
    protected void setUp() {
    }
    protected void tearDown() {
    }
    // there are normally many methods like this
    public void testX() {
    }
}

While there are other JUnit classes, TestCase is the workhorse. If you write tests to this pattern, Ant will grope around, find all public void methods whose names begin with test , and run them. That's all there is to it.

setUp

JUnit runs this method before running each of the tests.

Frequently you need to create a standard fixture to run your code against. This might be something fairly complex, perhaps an elaborate linked list or some other data structure used for exercising your code. Generally you will want a fresh copy of this for each test. You put the code for generating this fixture in setUp.

tearDown

This is the companion to setUp, run after each test to take down the test fixture.

In our experience tearDown is used much less commonly than setUp . It does no harm to leave a stub in your TestCase.

Assertions

Assertions are what JUnit is all about. The code you are testing is doing something. Your assertions will test that after running the software under test with a certain set of inputs, you will have a specified output. A typical assertion is something like

  // pattern:
  //   assertCondition(failureMsg, expected, actual);
  assertEquals("cube factory is failing", 8, cubeIt (2));

You can omit the message, but this is generally undesirable. To keep things rolling along, you want a clear and unambiguous description of what failed; if you don't describe the failure, when it does go wrong you will spend time trying to guess what actually failed, or you will have to go back and add the message.

There are many JUnit assertions, all of them documented in the Javadocs. In all cases, what is expected and the actual must be of the same type.

  • assertEquals
  • assertTrue
  • assertFalse
  • assertNull
  • assertNotNull
  • assertSame
  • assertNotSame
  • fail

All have two variants. For all except fail the first variant accepts two arguments (expected, actual) and the second three (failureMsg, expected, actual) .

In actual use, the most common assertion appears to be assertEquals .

We strongly recommend that instead of reading a lot about unit testing, you write a lot of test cases. After a while, it becomes second nature, and you have understood most of what is important regarding the subject.

Then it makes a lot of sense to go back and read about it.

Make Ant Do the Work for You

As your software becomes bigger, tests take longer to run and it also becomes more difficult to track exactly what is going on. Use Ant's filesets to maintain your focus.

This is easy to do if the software under test and the tests themselves are in parallel hierarchies. This allows you to casually switch between testing individual classes, testing all of the classes in a package, and running all of the tests, with just a few keystrokes in the editor:

  <batchtest todir="${test.report.dir}">
    <fileset dir="${test.src.dir}">
      <include name="**/TestStmtCoverage.java"/>
      <include name="**/TestRegistry.java"/>
    </fileset>
  </batchtest>
  
  <!-- other sets commonly tested; the first means 'all tests'
      <include name="**/*Test*.java"/>
      <include name="**/graph/Test*.java"/>
      <include name="**/TestA2O.java"/>
      <include name="**/TestBlockCode.java"/>
      <include name="**/TestClassFactory.java"/>
   -->

The build.xml fragment above runs only two tests, TestStmtCoverage and TestRegistry . If the first line in the commented group is copied into the fileset, it runs all tests.

Fixing Old Code

Life is more difficult if you are maintaining existing code. The practical approach is usually to introduce unit testing gradually, working towards the parallel directory hierarchies described above.

This often encounters a good deal of opposition, partially because of the short-term expense and partially because of the intellectual and political investment in the status quo.

If you persist, you will generally find that the code which most resists this approach is the code that most needs testing, because it is the buggiest.

More Information

There are two JUnit Web sites, one their own Web site and the other at Sourceforge . Both are very good. The first has links to Web sites describing dozens of other software packages using JUnit. The second is a tutorial with many references to other articles, papers, and Web sites.


This material is copyright 2003 by James David Dixon ("Jim Dixon") and is made available under the Artistic License .