3.16 Running Unit Tests

3.16.1 Problem

You want to run all of the unit tests in your project using Ant.

3.16.2 Solution

Follow a consistent naming convention for all of your test classes, and then use Ant's junit and batchtest tasks to locate and run the tests.

3.16.3 Discussion

Writing unit tests is a key XP practice, and Ant makes it easy to run those tests. A well-written buildfile should provide a target for running all tests in the project with a single command. In Example 3-7, programmers type ant junit to compile everything and then run all of the unit tests.

Example 3-7. Running unit tests
<?xml version="1.0"?>
<project name="Java XP Cookbook" default="compile" basedir=".">
  <property name="dir.build" value="build"/>
  <property name="dir.src" value="src"/>
  <property environment="env"/>

  <path id="classpath.project">
    <pathelement path="${dir.build}"/>
  </path>

  <target name="install.junit">
    <fail unless="env.JUNIT_HOME">
      The JUNIT_HOME environment variable must be set.
    </fail>

    <available property="junit.already.installed"
               file="${ant.home}/lib/junit.jar"/>

    <copy file="${env.JUNIT_HOME}/junit.jar"
          todir="${ant.home}/lib"
          failonerror="true"/>
   
    <fail unless="junit.already.installed">
      junit.jar was not found in ANT_HOME/lib prior to this 
      build, so it was copied for you. Please try your build again.
    </fail>
  </target>

  <target name="prepare" depends="install.junit">
    <mkdir dir="${dir.build}"/>
  </target>

  <target name="clean"
          description="Remove all generated files.">
    <delete dir="${dir.build}"/>
  </target>

  <target name="compile" depends="prepare"
          description="Compile all source code.">
    <javac srcdir="${dir.src}" destdir="${dir.build}">
      <classpath refid="classpath.project"/>
    </javac>
  </target>

  <target name="junit" depends="compile">
    <junit printsummary="on" 
           fork="false" 
           haltonfailure="false"
           failureproperty="tests.failed"
           showoutput="true">

      <classpath refid="classpath.project"/>
      <formatter type="brief" usefile="false"/>

      <batchtest>
        <fileset dir="${dir.src}">
          <include name="**/Test*.java"/>
        </fileset>
      </batchtest>
    </junit>

    <fail if="tests.failed">
    *******************************************************
    *******************************************************
    One or more tests failed. Check the output...
    *******************************************************
    *******************************************************
    </fail>
  </target>
</project>

This buildfile includes logic presented earlier in Recipe 3.15 that automatically installs junit.jar to the ANT_HOME/lib directory. Once this succeeds, we can proceed with the tests.

We use several attributes on the junit task to configure how Ant runs our tests. Table 3-3 outlines what each of the shown junit attributes means. This is only a subset of the available attributes; refer to the Ant documentation for a complete list of attributes.

Table 3-3. junit task attributes

Attribute

Description

printsummary="on"

Instructs Ant to print a one-line summary for each test as it runs. We recommend this setting so you get some sense of progress as your tests run.

fork="false"

Run the tests in the same JVM as Ant. This is the most efficient way to run your tests.

haltonfailure="false"

Do not abort the build if a test failure or error occurs.

failureproperty="test.failed"

If a test fails, set the "test.failed" Ant property. We will use this later to display a big error message that grabs the user's attention.

showoutput="true"

Print output from each test to the console.

In addition to XML attributes, the junit task contains several nested elements. The classpath element, as you might expect, defines where the classes for your tests are found. In this case, we reference the project-wide classpath defined earlier in the buildfile.

The formatter element defines how test results are formatted:

<formatter type="brief" usefile="false"/>

The available formatter types are brief, plain, and xml. By specifying usefile="false", we indicate that output should go to the console rather than a file. The brief formatter is the most concise, providing information about tests that fail. The plain formatter shows statistics for every test in text format, and the xml formatter is useful for converting test output to other forms such as HTML. We will see how to use xml output in Recipe 3.18.

Finally, we use a nested batchtest element to select which tests are actually executed:

<batchtest>
  <fileset dir="${dir.src}">
    <include name="**/Test*.java"/>
  </fileset>
</batchtest>

The batchtest element selects a set of files using a specified naming convention. In our case, we include all files named Test*.java found under the source directory. Once these files are located, batchtest converts the filenames into Java classnames, passing those to the junit task where they are executed.

3.16.4 See Also

The next recipe shows how to run a single test, rather than all tests. Ant and JUnit may also be used for other kinds of tests. For instance, you may provide targets for customer acceptance tests that also use the JUnit framework.