The Life Cycle of a Test Suite

The dynamic side of a program is hard to grasp by the sole study of class diagrams. Consider the following AllTests class:

public class AllTests {
 public static void main(String[] args) {;
 public static Test suite() {
 TestSuite suite = new TestSuite("All tests of MyProject");
 return suite;

Look what happens when we call this class from the command line:

  1. The AllTests class itself is used as an argument to start the AWT test runner.

  2. The test runner calls the static suite() method of the AllTests class. The latter, in turn, builds a complex test suite in tree structure by adding other test suites—in this case pack1.AllTests and pack2.ExampleTest—to its own. The difference between addTest(Test) and addTestSuite(Class) is that addTestSuite(Class) asks the class passed on—a subclass of TestCase—for its test methods, using Java's reflection mechanics, and uses them to build a suite. At the ends of this building chain, there are individual test cases. Each test case is represented by an instance of the class TestCase or a subclass. For example,
    public class ExampleTest extends TestCase {
     public void testFoo() {...}
     public void testBar() {...}
     public void testFooBar() {...}

    becomes an instance of TestSuite, with three instances of the ExampleTest class, which differ only in the name of the called test method. This name is stored internally as the name of the test case and can be retrieved by getName().

  3. Clicking the Run button first generates a TestResult instance and then starts the run(TestResult) method of the top test suite. This test suite, in turn, calls the run(TestResult) method of all involved tests sequentially. In the "leaves" of this call tree—the TestCase instances—run(TestResult) first causes the execution of setUp() and then the runTest(TestResult) method, followed by tearDown(). Unless runTest() was explicitly overloaded, it will now start the actual test method; it learned the name of this method from the constructor's String parameter. Screenshot shows a simplified process from the beginning to the execution of a test case. [2] Java Click To expand
    Screenshot: Sequence diagram for the start of a test case.

We can learn two things from the previous process description: First, it makes a difference whether we build the test fixture—normally identical to the instance variables of the test class—in the constructor or in the setUp()method, because a test setup should occur only during the execution of a test. Second, the tests are executed sequentially, but not in a defined order, so that we cannot rely on it.

What will happen when we click the Run button a second time depends on the status of the Reload checkbox. If the Reload option is deselected, then another run(TestResult) is sent to the top test suite. In contrast, if Reload is selected, all classes are reloaded, [3] the test tree is created again, and finally the suite is started. We need to know that the instances of the test cases have to be such that they could be restarted repeatedly. [2]As usual, reality is a little more complicated, and the details are best taken from the JUnit source code itself. [3]Dynamic reloading may cause problems (see JUnit FAQs in Appendix A, Section A.1).