OpenEJB - Container System                                   David Blevins
Contributor's Guide: CG-1                                        July 2001


                Contributor's Guide -- Unit Tests


Abstract

  The perfect place to get involved and gently brought up to speed is by  creating unit tests.  The unit tests are a fabulous way to get started because it gets you right into the core code right away without needing any knowledge in the overall design.  This document details the guidelines for creating unit tests.

Table of Contents
  
  1  Introduction
  2  Package name
  3  Class name
  4  Location
  5  Superclass and Constructor
  6  Testing Constructors
  6.1  Test Method Names for Constructors
  6.2  Classes with overloaded constructors
  7  Testing Methods
  7.1  Test Method Names
  7.2  Overloaded methods
  9  Test Method Body
  9.1  Passing and Failing
  9.2  Exceptions
  10  Uncovering Bugs
  11  Unfinished Tests
  12  Working on a Test
  13  Other Resources


1  Introduction
  
In the OpenEJB world, unit tests test a specific method or constructor of a specific class.  Ideally, every class in OpenEJB should have an equivalent test class with test methods (unit tests) testing all the constructors and methods of that class.  Some methods may execute differently based upon the input, multiple unit tests should be written to exercise each aspect the method to ensure complete coverage of the methods functionality. 

2  Package name

Tests go in the same package as the class being tested.  This allows the unit tests in the test class to have package level, protected and public access to the methods in the class being tested.

If we had a class called MyClass in the package org.openejb.foo, the test class would declare org.openejb.foo as its package.

3  Class name

Test class names follow this format: 

UT<className>

UT is a prefix to quickly distinguish the that the class is a unit test and not part of the API.

<className> is the name of the class being tested.

Example:

org.openejb.foo.MyClass --> org.openejb.foo.UTMyClass

4  Location

All test classes are to be placed under the test/src/ directory of the repository regardless of where the class being tested is located.

5  Superclass and Constructor

All test classes must extend subclass from org.openejb.test.NamedTestCase

There must be a public no-arg constructor.  In the constructor, the superclass's constructor must be called passing in the name of the class being tested followed by a period as a string.

Example:

package org.openejb.foo;

import org.openejb.test.NamedTestCase;

public class UTMyClass extends NamedTestCase{

    public UTMyClass(){
        super("org.openejb.foo.MyClass.");
    }
}


6  Testing Constructors

All constructors of a class must be tested before testing individual methods.

6.1  Test Method Names for Constructors

All test methods (unit tests) should be declared as public, return void, and have no arguments.  Test method names for testing a classes constructor follow this format: 

public void test<XX>_constructor()

test -- A prefix which signifies that the method in the test class is a unit test.  Methods with this prefix are automatically collected, sorted and executed when the test suite is ran.

<XX> -- This should be replaced with a two digit number that dictates the order in which the test will be executed in reference to the other unit tests in the test class.

constructor -- A suffix denoting that the unit test is testing a constructor of the class being tested.

Example:

public void test01_constructor(){...}

6.2  Classes with overloaded constructors

To test a constructor more than once or to test multiple signatures in the case of overloaded constructors, used the naming format above with a number appended to the test method signature.

For example, MyClass has two constructors: MyClass(Integer i) and MyClass(int i).  This requires at least two test methods:

test01_constructor1() {...} 
test02_constructor2() {...}

7  Testing Methods

Each method should be tested starting with the simplest method working up to the more complicated/interrelated ones.  For example, if methodC calls methodA and methodB, test methodA and methodB before methodC.

7.1  Test Method Names

All test methods (unit tests) should be declared as public, return void, and have no arguments.  Test method names follow this format: 

public void test<XX>_<methodName>()

test -- Same as above.
<XX> -- Same as above.
<methodName> -- The method name of the method in the class being tested

For example, the method myMethod of MyClass might have a unit test method like the following in the class UTMyClass:

public void test05_myMethod(){...}

7.2  Overloaded methods

To test a method more than once or to test multiple signatures for overloaded methods, used the naming format above with a number appended to the test method signature.

For example, myMethod has two signatures: myMethod(Integer i) and myMethod(int i).  This requires at least two test methods, once for each signature.

public void test05_myMethod1(){...}
public void test06_myMethod2(){...}

9  Test Method Body 

9.1  Passing and Failing 

Use the junit.framework.Assert class to assert method results. ALWAYS include a descriptive message string for the methods assert(...), assertNotNull(...), and assertNull(...).

Example:

public void test06_myMethod1(){

    Integer excepted = new Integer(4);
    Integer actual = classInstance.myMethod(new Integer(3));

    Assert.assertNotNull(actual,"Null string!!!");
    Assert.assertEquals(expected, actual);
}

9.2  Exceptions

All testing code should be placed inside a try and catch block to exceptions from leaving the test method and being logged as unknown errors by the testing framework. If an unexpected exception is thrown, the test must be failed with sufficient message to find the problem.

Example:

public void test06_myMethod1(){

    try{

        Integer excepted = new Integer(4);
        Integer actual = classInstance.myMethod(new Integer(3));

        Assert.assertNotNull(actual,"Null string!!!");
        Assert.assertEquals(expected, actual);
    } catch (Exception e){
        Assert.assert("Received Exception "+
                       e.getClass()+ " : " +
                       e.getMessage(), false);
    }

}

10  Uncovering Bugs

If the test methods runs and uncovers a bug, prefix the test method name with "BUG_" as in "BUG_test06_myMethod1()".  This serves a few important functions:

-  Prevents the test suite output from being deluded with reports from known bugs.  

-  Allows us to easily locate known bugs in the code and even write scripts that generate a bug list by searching test files for "BUG_".

-  Removing the "BUG_" prefix gives developers an easy way to put the test back into the suite for debugging.

11  Unfinished Tests

Tests that aren't finished or fail due to problems in testing code --NOT the code of the class being tested-- should be prefixed with "TODO_" as in  "TODO_test06_myMethod1()"

12  Working on a Test

If you will be working on a test already in the repository for an extended  period of time for debugging or other reasons, it may be useful to prefix your initials in capitol letters to the test method name as in "DMB_test06_myMethod1()".  This lets others know not to spend time working on what is already covered.  This can be done in addition to one of the "BUG_" or "TODO_" prefixes above as in "DMB_TODO_test06_myMethod1()".

Similarly, if you wish to assign a test to someone for completion or debugging, you may prefix their initials to the test method name as in "RMH_test06_myMethod1()".  This can be done in addition to one of the "BUG_" or "TODO_" prefixes above as in "RMH_BUG_test06_myMethod1()".

Once the prefix is added, simply check the file in so others are aware of what you are working on or what they should be working on.  If you don't have CVS access to commit files, you can send an email like the following to the OpenEJB-CVS@exolab.org mailing list:

TO: OpenEJB-CVS@exolab.org
FROM: you@yourdomain.com
SUBJECT: org.openejb.foo.MyClass.DMB_TODO_test06_myMethod1()


13  Other Resources

  www.junit.org

