A Whirlwind Introduction to JUnit

Writing your own software tests in Java is simple and clean. This guide will show you how to simplify some of the less common tasks as well.

Background info:

For student use, we provide a supplemental library (already available to all assignments on Web-CAT) that enhances basic JUnit features in a number of ways that will make your life easier. There are two steps to using this support: make sure the appropriate jar files are on your classpath, and then use a specific base class for all your test cases (even if you are using JUnit 4 techniques--use the provided base class anyway).

To set up your classpath, download the following 2 jar files:

Place both jars on your project build path in the order listed. If you're using Eclipse and you've already added Eclipse's built-in JUnit library to your project, ensure that these two libraries come before Eclipse's in the library search order--or just remove Eclipse's JUnit library from the build path entirely.

All the test classes you create should then extend the base class student.TestCase. This class is itself a custom subclass of junit.framework.TestCase, so all normal JUnit practices still work. The advantages of using this subclass include:

For full details, see the Javadoc documentation for student.TestCase.

1. import

Import the JUnit classes so you can use them in your code.

import student.TestCase;
...

2. create your class

Your class should be named using the JUnit convention with the name of the class you are testing followed by the word Test.

If you class is named Student, then your test will be named StudentTest.

import student.TestCase;

public class StudentTest
    extends TestCase
{
    // code goes here...
}

3. define your test cases

Test cases are defined using methods with names that start with test...().

Typically, your method will be named following the method you are testing in your source code. The example to the right tests the setName() method, so it is called testSetName(). For more complicated methods, you may have many tests (e.g., testFoo1(), testFoo2(), testFoo3(), etc.).

You have to test your assumptions/expectations of your code using an assertXXX() method call. These are provided by the TestCase base class.

import student.TestCase;

public class StudentTest
    extends TestCase
{
    private Student aStudent;

    public void setUp()
    {
        // fixture to be used for testing
        aStudent = new Student("Joe", "888-2993");
      }

    public void testSetName()
    {
        aStudent.setName("Manuel");
        assertEquals(aStudent.getName(), "Manuel");
    }
}

The most common assert...() methods you will use to express the expected outcomes in a test case are:

MethodMeaning
assertEquals(xy); Assert that two values are equal, using the equals(Object) method to compare them. This is the most commonly used assert method in your arsenal.
assertEquals(xydelta); Assert that two floating point values are equal, within some tolerance delta. Because of the inexactness of floating point representations, it is incorrect to test that two floating point values are equal using numeric equality (==). Fortunately, if you try this while using student.TestCase, you'll get an appropriate diagnostic error. Instead, always specify a tolerance value: how close to the expected value does the computed number have to be?
assertTrue(condition); Assert that a boolean condition is true.
assertFalse(condition); Assert that a boolean condition is false.
assertNull(x); Assert that a reference is null.
assertNotNull(x); Assert that a reference is not null.
assertSame(xy); Assert that two references refer to the same destination (i.e., two variables refer to the same object). This is the same as performing an == equality comparison between reference variables.

There are many more assert methods than those listed here, but these are by far the most common.

1. import and create your test class

Import the JUnit classes so you can use them in your code.

import student.TestCase;

public class StudentTest
    extends TestCase
{
    // code goes here...
}

2. define your fixtures

Fixtures are objects that are used in your test cases.

Define your fixtures as private fields in your test class (instance variables).

Initialize your fixture in the setUp() method.

The setUp() method is called automatically before each test method is executed, to ensure each test operates on a clean set of freshly initialized objects.

import student.TestCase;

public class StudentTest
    extends TestCase
{
    private Student aStudent;  // fixture to be used for testing

    public void setUp()
    {
        // initialize it here
        aStudent = new Student("Joe", "888-2993");
    }
}

3. use fixtures in your test cases

The two test cases on the right run succesfully, since they each start with a freshly generated copy of the aStudent object.

...
    public void testSetName()
    {
        assertEquals(aStudent.getName(), "Joe");

        aStudent.setName("Manuel");
        assertEquals(aStudent.getName(), "Manuel");
    }

    public void testSomethingElse()
    {
        // aStudent .. is new here
        assertEquals(aStudent.getName(), "Joe");
        assertEquals(aStudent.getID(), "888-2993");
    }
}

Consider this simple piece of code.

public class HelloWorld
{
    public static void main(String[] args)
    {
        System.out.println("Hello world!");
    }
}

1. call main() like any other method

Themain() method is just a static method, so you can call it directly like any other. Don't forget its argments!

import student.TestCase;

public class HelloWorldTest
    extends TestCase
{
    public void testMain()
    {
        // call main()!
        HelloWorld.main(null);
    }
}

2. refer to the history of System.out

The student.TestCase base class provides for capturing all output sent to System.out during a test case. The history contents are reset before every test method automatically.

Note that if you are using assertEquals(), the expected output must match exactly, character for character, including all newlines.

Also note that systemOut()'s history is automatically normalized to represent newlines as "\n", regardless of platform, so just use \n in any expected values.

    public void testMain()
    {
        HelloWorld.main(null);

        // Can combine these into one line if needed
        String output = systemOut().getHistory();
        assertEquals("Hello world!\n", output);
    }

3. use fuzzy matches when details are less important

The student.TestCase base class provides an assert method called assertFuzzyEquals() that performs more lenient string comparisons. It ignores all differences in capitalization, punctuation, or extra spacing, as well as all whitespace at the beginning and ending of strings.

    public void testMain()
    {
        HelloWorld.main(null);
        assertFuzzyEquals("hello world", systemOut().getHistory();
    }

Consider this simple piece of code.

public class HelloWorld
{
    public static void main(String[] args)
    {
        System.out.println("Hello, " + args[0] + "!");
    }
}

1. just provide arguments when invoking main()

You can provide any arguments you want when you call main() in a test case.

    public void testMain()
    {
        HelloWorld.main(new String[] { "Joe" });
        assertEquals("Hello, Joe!\n", systemOut().getHistory());
    }

Consider this simple piece of code.

public class HelloWorld
{
    public static void main(String[] args)
    {
        System.out.println("Hello, " + args[0] + "!");
        System.exit(0);
    }
}

1. exit calls get turned into exceptions

You can provide any arguments you want when you call main() in a test case.

    public void testMain()
    {
        try
        {
            HelloWorld.main(new String[] { "Joe" });
        }
        catch (student.testingsupport.ExitCalledException e)
        {
            // Confirm exit's return value was zero:
            assertEquals(0, e.getStatus());
        }
        // Check the System.out contents, too:
        assertEquals("Hello, Joe!\n", systemOut().getHistory());
    }

Consider this simple piece of code.

import java.util.Scanner;

public class HelloWorld
{
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);

        System.out.print("Enter your name: ");
        String name = in.next();
        System.out.println("Hello, " + name + "!");
    }
}

1. use setSystemIn() to set the contents of System.in

You can provide your own contents for System.in within your test case if you are using student.TestCase.

    public void testMain()
    {
        setSystemIn("Joe\n");  // Don't forget the newline!
        HelloWorld.main(null);
        assertEquals("Enter your name: Hello, Joe!\n",
            systemOut().getHistory());
    }

2. sometimes, you don't want to test all of the output

Notice that the assert method call in the example shown above includes the full text, including the prompt message. In fact, the prompt message is followed by a space, because that space is part of the prompt itself, but it isn't followed by a newline (the newline is part of what is typed on stdin, not part of what is "printed" by the program). This can be a bit confusing if you have many back-and-forth prompts and responses in a test case action.

Sometimes, it is better to use contains() or endsWith(), rather than testing the entire output using assertEquals().

    public void testMain()
    {
        setSystemIn("Joe\n");
        HelloWorld.main(null);
        asserttrue(systemOut().getHistory().contains("Hello, Joe!"));
    }
  1. JUnit.org has more information on how to use JUnit.

  2. Documentation for the asserts in JUnit: http://www.junit.org/apidocs/org/junit/Assert.html

  3. Testing GUI Programs is a Prezi presentation that gives a good introduction to LIFT and how it is used.

  4. The LIFT website provides more details on LIFT, including links to two sigcse papers, downloads, examples, and discussion forums.