How To Test With JUnit

JUnit is a widely used unit testing framework for the Java programming language. It is a member of the xUnit family of frameworks that support a wide variety of programming languages and is commonly used when practising TDD (test-driven development).

Take a moment, if necessary, to familiarise yourself with the various terms and concepts in the previous paragraph.

Environment

Because JUnit is not part of the Java Standard Library, you will need access to its source to be able to use it. I have placed a public copy of junit-4.12.jar and hamcrest-core-1.3.jar in my account which you may use (when logged into the Computer Science servers or workstations). The examples below will give you the details necessary to do so. If you would like to use JUnit on your own machine, you can download the necessary files from the junit.org web site.

A TDD Example

Imagine you were going to write a simple class, OddEven, with methods to tell you if a value is odd or even. The class will have a single constructor accepting an integer value and two boolean methods, odd and even that return true or false as appropriate. Following TDD, you would want to:

  1. Develop some test cases for a small part of your code.
  2. Write the test cases.
  3. Write the code to be tested.
  4. Run the tests.
    • Fail? Go back to step 3.
    • Pass? Continue at step 1 with the next part of your code.

For this class, I think a minimum of five values should be tested, two positive integers, one even and one odd, two negative integers, one even and one odd, and zero. Here is a table of the test cases:

Constructor Input Expected Result for even() Expected Result for odd()
86 true false
125 false true
−62 true false
−3 false true
0 true false

Now we write the test cases in Java using JUnit: OddEvenTests. Be sure to read all of the comments as they explain much of the syntax for using JUnit.

Next up, we write our OddEven class. Note that there is a bug in the solution; don't fix it until after you see how JUnit reports the failed tests.

Compiling and Running the Tests

To compile and run the tests with JUnit, you must specify where to find the JAR files containing the JUnit classes. This is done by modifying the classpath which may be done in two ways. The first way is to specify the path by using the -cp flag as follows (be sure to include the colon and period at the end of the -cp path when running):

$ javac -cp /home/phatalsk/public/misc/junit-4.12.jar OddEven.java OddEvenTests.java
$ java -cp /home/phatalsk/public/misc/hamcrest-core-1.3.jar:/home/phatalsk/public/misc/junit-4.12.jar:. org.junit.runner.JUnitCore OddEvenTests

This is rather tedious to type out every time we want to use JUnit. The second way is to set the CLASSPATH environment variable. When you do this, you do not need to include the -cp flag as shown above. Setting the CLASSPATH environment variable varies by operating system. In Linux, you would do the following:

$ export CLASSPATH="$CLASSPATH:/home/phatalsk/public/misc/hamcrest-core-1.3.jar:/home/phatalsk/public/misc/junit-4.12.jar:."

Be sure to include the period and colon at the beginning of the path! Note that you would have to do this once every time you log on. To automate the process, you can add the line above to your .mybashrc. See me in lab if you would like help with this.

Reading the Output

Assuming you didn't fix the bug in OddEven, when you finally run the tests, you probably saw something that looked like:

   ...
   There were 2 failures:
   1) testOdd_negativeOdd(OddEvenTests)
   java.lang.AssertionError
           at org.junit.Assert.fail(Assert.java:86)
           at org.junit.Assert.assertTrue(Assert.java:41)
           at org.junit.Assert.assertTrue(Assert.java:52)
           at OddEvenTests.oddTestNegative2(OddEvenTests.java:55)
           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   ...
   2) testEven_negativeOdd(OddEvenTests)
   java.lang.AssertionError
           at org.junit.Assert.fail(Assert.java:86)
           at org.junit.Assert.assertTrue(Assert.java:41)
           at org.junit.Assert.assertFalse(Assert.java:64)
           at org.junit.Assert.assertFalse(Assert.java:74)
           at OddEvenTests.evenTestNegative2(OddEvenTests.java:75)
           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   ...
   FAILURES!!!
   Tests run: 10,  Failures: 2

The information that spews forth is the stack trace. It is a list of all the methods that have been called leading up the error. All we really need to get out of all this is that two tests of the ten failed, oddTestNegative2 and evenTestNegative2. See if you can reason out why, fix the bug, rerun the tests, and get the output to look like:

   JUnit version 4.12
   ..........
   Time: 0.007

   OK (10 tests)

Running Multiple Test Classes

You can compile and run multiple JUnit test classes at the same time (for programs comprised of more than one class). To do so, you simply include the names of all the test classes on the command line as follows:

$ java org.junit.runner.JUnitCore <Class1> <Class2> ...

Testing for Expected Exceptions

It is important to test methods that throw exceptions to make sure they actually throw them as expected. The JUnit @Test annotation has an optional form that allows you to specify an expected exception. When the test if run, JUnit reports success if the expected exception is thrown and a failure otherwise. Don't forget to import the exception class as necessary. Here is a sample test method for a method that should throw an IllegalArgumentException when passed a negative number:


   @Test(expected=IllegalArgumentException.class)
   public void someTest() {
      // Test setup...
      SomeClass sc = new SomeClass();

      // The test, call the method in a way that should result in the expected
      // exception
      sc.someMethod(-8);
   }

JUnit Quirks

To learn more about JUnit—it has much more functionality than is explored here—you are strongly encouraged to explore the JUnit documentation, especially that of the Assert class to see the many useful testing methods. More information is also available at junit.org.