Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

TestNG: The next generation of unit testing

Leverage TestNG for unit, synchronous, asynchronous, and parallel testing

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

TestNG, written by Cedric Beust and Alexandru Popescu, is a light framework based on Java annotations (for J2SE 5.0) that allows you to design complex unit testing for J2SE 5.0 and J2SE 1.4. Why bother learning another unit-testing framework when you're already comfortable using JUnit? If you are interested in simplifying your unit-test cases, in leveraging J2SE 5.0 annotations to tag your test classes as well as being backward compatible with J2SE 1.4, in having out-of-the-box support for dependent methods and parallel and asynchronous testing, TestNG is the tool you are looking for.

This article starts with a description of the JUnit annoyances and introduces TestNG through numerous examples. A case study shows how to use TestNG for asynchronous testing.

JUnit annoyances

The JUnit testing framework has been around for a long time and is (hopefully) widely used by Java developers to unit test developed software. However, the framework has some annoying specificities, which the following sections describe.

Multiple instantiations per TestCase

Multiple TestCase instantiations is a well-known issue. The setup() and teardown() methods are called before and after each test method; moreover, the test class that must extend TestCase is also instantiated each time a test method is called. Although multiple TestCase instantiations might prove acceptable for simple test cases, what if you want to set up an object that is to be reused across more than one test method, for example, a Java Database Connectivity connection (or, for that matter, a Java Naming and Directory Interface (JNDI) context)?

PainOneTest.java illustrates the problem:

 public class PainOneTest extends TestCase {
  /**
   * PainOneTest
   */
  public PainOneTest() {
    System.out.println("PainOneTest");
  }

/** * @see TestCase#setUp() */ protected void setUp() throws Exception { System.out.println("setUp()"); }

/** * @see TestCase#tearDown() */ protected void tearDown() throws Exception { System.out.println("tearDown()"); }

/** * testMe */ public void testMe() { System.out.println("testMe"); }



/** * testYou */ public void testYou() { System.out.println("testYou"); } }


This code produces the following output:

 PainOneTest
PainOneTest
setUp()
testMe
tearDown()
setUp()
testYou
tearDown()



The TestSetup class circumvents this issue, as illustrated below:

 public class PainTwoTest extends TestCase {
  /**
   * Test
   * @return
   */
  public static Test suite() {
    return new TestSetup(new TestSuite(PainTwoTest.class)) {
      public void setUp() throws Exception {
        System.out.println("setUp()");
      }

public void tearDown() throws Exception { System.out.println("tearDown()"); } }; }

/** * PainOneTest */ public PainTwoTest() { System.out.println("PainOneTest"); }

/** * testMe */ public void testMe() { System.out.println("testMe"); }

/** * testYou */ public void testYou() { System.out.println("testYou"); }

/** * main * @param _ */ public static void main(String []_) { Test test = PainTwoTest.suite(); } }


This code produces:

 PainOneTest
PainOneTest
setUp()
testMe
testYou
tearDown()



The above solution introduces a static method, and all variables accessed by it must also be declared static. Although I have nothing in particular against static methods, you must take care when implementing test methods for concurrency access (sharing static objects) and static initialization (occurring only once per VM).

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources