4.12 Unit Test Organization

4.12.1 Problem

You want to organize all of your tests consistently.

4.12.2 Solution

Create a test case that runs all tests in the current package and subpackages. Duplicate this pattern for all packages in your application.

Some Java IDEs allow you to automatically run all tests in your project or in a specific package, negating the need for this recipe.

4.12.3 Discussion

Example 4-5 shows an example of the technique outlined in the solution just presented. It runs all of the test suites in the current package as well as delegating to each AllTests class in immediate subpackages.

Example 4-5. AllTests example
package com.oreilly.javaxp.junit;

import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
                                   
/**
 * Runs all test suites in the current package and sub-packages.
 */ 
public class AllTests extends TestCase {
    /**
     * @return a suite containing all tests in this package
     *         and subpackages.
     */
    public static Test suite(  ) {
        TestSuite suite = new TestSuite(  );

        // add tests from the current directory. This requires manual
        // updates, which is the main weakness of this technique
        suite.addTest(new TestSuite(TestGame.class));
        suite.addTest(new TestSuite(TestPerson.class));

        // add AllTests from any sub-packages
        suite.addTest(com.oreilly.javaxp.junit.sub.AllTests.suite(  ));
        // suite.addTest(...) // continue for other sub-packages

        return suite;
    }
}

This technique can be useful when using an IDE[9] because you can select any AllTests class and run tests for a subset of your project. Assuming that you follow this pattern consistently, you can run the AllTests in your root directory to run every test in your application.

[9] IntelliJ IDEA allows you to right-click on any directory and run all tests in that package, thus eliminating the need to manually create an AllTests class.

AllTests intentionally avoids the TestXXX naming convention outlined in Recipe 4.11. This prevents the AllTests from being executed when you tell Ant to find and run all TestXXX classes.

Human fallibility is the main weakness of this technique. If you are not diligent, you will forget to add some tests to one of the AllTests classes. This can be overcome by writing a utility to automatically generate the AllTests classes. Yet another technique is to do the same thing dynamically: to write a class that sifts through a directory/package looking for TestXXXX classes and including them in the suite.

You might also want to consider whether the AllTests classes should run tests in subpackages, or just the current package. Here is a modification that allows you to choose the behavior you want based on a system property:

public static Test suite(  ) {
    TestSuite suite = new TestSuite(  );
    // add tests from the current directory
    suite.addTest(new TestSuite(TestGame.class));
    suite.addTest(new TestSuite(TestPerson.class));

    // only test subdirectories if a system property is true
    if ("true".equals(System.getProperty("test.subdirs"))) {
        // add AllTests from any sub-packages
        suite.addTest(com.oreilly.javaxp.junit.sub.AllTests.suite(  ));
        // suite.addTest(...) // continue for other sub-packages
    }
            
    return suite;
}

4.12.4 See Also

Recipe 3.16 shows an example of Ant's batchtest element.