February 27, 2021
Hot Topics:

Test Cases Made Easy with JUnit 4.5

  • By David Thurmond
  • Send Email »
  • More Articles »

Another complex situation that is easily addressed in JUnit 4.5 is when a method fails to complete in a timely manner. This might be due to network latency, an inefficient algorithm, or perhaps even the ever-dreaded infinite loop. Consider the test case below for the NumberCruncher class's appropriately-named doForever() method:

   @Test (timeout = 5000)
   public void testDoForever() {
      NumberCruncher nc = new NumberCruncher();
      assertEquals(new Integer(0), nc.doForever());
   }    // testDoForever()

Here, the timeout= attribute tells JUnit to wait up to 5000 milliseconds for the doForever() method to return, after which the test case fails.

Checking for timeouts was not even a part of the original JUnit framework. Thus, performing this same check in JUnit 3.8 and earlier would mean doing some home-grown coding to kick off the test as a separate thread and notify the caller when the test took too long. As with the exception-handling example earlier, it is best for the test case itself to be as simple as possible, so the approach used in JUnit 4.5 is practically foolproof by comparison.

Another handy bit of functionality added in JUnit 4.5 is the new assertEquals() method that accepts array arguments. Consider the JUnit 3.8 test case below for testing the NumberCruncher's getFactors() method:

public void testGetFactors() {
      NumberCruncher nc = new NumberCruncher();
      Object[] f1 = nc.getFactors(new Integer(32));
      Object[] f2 = nc.getFactors(new Integer(32));
      assertTrue("Factor Arrays Equal Length",
                 f1.length == f2.length);
      for (int i = 0; i < f1.length; i++) {
         assertTrue("Array Element " + i + " Is Equal",
      }    // for i
   }    // testGetFactors()

As you can see, the only way to verify whether the factors returned in the arrays are really identical is to check the array lengths to see if they are equal and to loop through all of the elements of the arrays, comparing each element individually. This is somewhat tedious, and possibly error-prone. JUnit 4.5 has addressed this problem, as you can see from the following:

   public void testGetFactors() {
      NumberCruncher nc = new NumberCruncher();
      Object[] f1 = nc.getFactors(new Integer(32));
      Object[] f2 = nc.getFactors(new Integer(32));
      assertEquals("Factors are equal", f1, f2);
   }    // testGetFactors()

The array length assertion and loop in the previous example are now replaced with a single assertion that takes the two arrays as arguments. Thus, the complexity of writing the test case itself is removed in JUnit 4.5, hopefully leading to bug-free test cases.

Writing a Test Suite

Groups of test cases are commonly referred to as a test suite. They often test blocks of related functionality within a system that depend on the same code. Listing 3 below shows how a test suite would be coded in JUnit 3.8:

Listing 3: NumberCruncherTestSuite38.java

package com.dlt.developer.junit;
import junit.framework.*;

public class NumberCruncherTestSuite38 {
   public static Test suite() {
      TestSuite suite = new TestSuite();
      suite.addTest(new TestSuite(NumberCruncherTestCase38.class));

      return suite;
   }    // suite()

}    // NumberCruncherTestSuite38

This code relies on the junit.framework.TestSuite class. Here, a TestSuite object is created, and one or more individual test case classes are added to the suite. The static suite() method returns the TestSuite object for the JUnit test runner to use in order to invoke the various test cases.

In JUnit 4.5, annotations are used to perform the same functions. Listing 3.1 demonstrates this.

Listing 3.1: NumberCruncherTestSuite45.java

package com.dlt.developer.junit;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

public class NumberCruncherTestSuite45 {

}    // NumberCruncherTestSuite45

Here, the test suite is essentially an empty class declaration with the desired test case classes added using the @Suite annotation. This annotation associates the test case class with the test case runner, which, in this case, is the org.junit.runners.Suite class, as specified with the @RunWith annotation. You can specify any of the other specialized test case runners found in the org.junit.runners package on the @RunWith annotation as well.

Although the test suite class above is an empty shell, this does not have to be the case. Again, as with the test cases, the test suite actually can be integrated into the business layer of an application without any impact to the functionality of the business objects.

So, what should you do if you need to run JUnit 4.5 test cases using JUnit 3.8? Luckily, JUnit provides an adapter class for this purpose. To use the adapter, create the usual JUnit 3.8 test suite class above, but code the suite() method as follows:

public static junit.framework.Test suite() {
   return new JUnit4TestAdapter(NumberCruncherTestCase45.class);
}    // suite()

As you can see above, the adapter class takes the JUnit 4.5 test case class and allows a JUnit 3.8 test runner to invoke the proper test case methods automatically.

Page 4 of 5

This article was originally published on November 12, 2008

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date