<< 2008-9 >>
Department of
Computer Science
 

JUnit in the Computer Science Department

JUnit is a tool for writing tests, designed to fit in with the extreme programming philosopy of testing during development. You can download and install JUnit for yourself from:

the JUnit site

The site includes API documentation for the latest version (3.8.1 at the time of writing) and various tutorials:

API Documentation for version 3.8.1
Tutorial documentation

How JUnit is Installed in Computer Science

Like a lot of Java based developer's tools, the JUnit documentation tells you to change your classpath globally. Global environment variables are terrible (see footnote) so JUnit has been set up in Computer Science with a junit command which you can use in three ways:

The CS way        The official way                    What it does

                  export CLASSPATH=.../junit.jar      set up classpath
junit Test.java   javac Test.java                     compile a test class
junit Test        java junit.textui.TestRunner Test   run a test class
junit             java junit.swingui.TestRunner       start up the JUnit GUI

You can still use the official route, if you can find out where the junit.jar file is installed (e.g. /usr/local/src/junit3.8.1/junit.jar on Linux, at the time of writing).

Installing JUnit Yourself

Perhaps the easiest way is to use the eclipse IDE which has JUnit built in. Eclipse is installed on the departmental machines, and some introductory documentation is available here. Otherwise, Download the JUnit zip file, unzip it, and put the junit.jar file somewhere sensible.

If you use Linux, you could copy and adapt the junit shell script from Computer Science. You can find out where it is using which junit.

If you use Windows, you could do the same with a batch script (which you would have to write), or you could set up your preferred Java tool (e.g. Textpad) to do the right thing. If all else fails, you can use the official route and change your CLASSPATH environment variable globally.

How you get at the environment variables is different on different versions of Windows, e.g. Control Panel -> System -> Environment Variables, or Control Panel -> System -> Advanced -> Environment Variables, or My Computer -> right click -> Properties -> Advanced -> Environment Variables, or Start -> Programs -> Accessories -> System Tools -> System Information -> Software Environment -> Environment Variables, or some other variation.

Then change the CLASSPATH variable. If there isn't one, create one. It's value has to be typed by hand, and it must be character-perfect. The value should be .;C:\junit\junit.jar which is a dot followed by a semicolon followed by the full path of your junit.jar file, with no spaces. (It is safer not to have any spaces in the path to your file, but if you must, then the CLASSPATH value must be suitably quoted.) If you already have a CLASSPATH, leave it intact and add a semicolon plus your path.

Using Junit

There are problems with the official documentation (at the time of writing). First, the tutorials don't start at the beginning by telling you how to write, compile and run tests. Second, they have lots of stuff about the background extreme programming philosophy and about how JUnit is implemented, mixed in with how to use it. Third, some of the material is out of date. So, here's yet another quick tutorial.

Getting Started

Getting started using v3.0+ of Eclipse is particularly easy (notes on how to use Eclipse)... you can create your own JUnit tests from within the IDE, by using the File -> New -> Other -> Java -> JUnit test case or JUnit test suite. A wizard will help you create your test case or suite, then you can create your Java test code using the editor. To run your tests, select the test case's or test suite's Java file in Eclipse's package explorer, right-click and select Run -> JUnit test.

Here is a minimal example to get started. First here's a class with one method that needs testing, and a program to test it:

Example.java
ExampleTest.java

Download these files and try them out with:

junit ExampleTest.java   or   javac ExampleTest.java
junit ExampleTest        or   java junit.textui.TestRunner ExampleTest
junit                    or   java junit.swingui.TestRunner

The first command compiles the test program, and the second runs it. The third starts up the GUI, so you can run the tests repeatedly while developing the program.

The Example Explained

The program ExampleTest looks like this:

import junit.framework.*;

public class ExampleTest extends TestCase
{  
   public void testTail()
   {
      Example example = new Example();
      String s = example.tail("steal");
      assertEquals("teal", s);
   }

   public static Test suite()
   {
      return new TestSuite(ExampleTest.class);
   }

   public static void main(String args[])
   {
      junit.textui.TestRunner.run(suite());
   }
}

The red parts are always the same. They link the program with the JUnit library. Each test is a separate method with a name that starts with the four characters test... The suite method tells JUnit to look through the code for method names that start with test... and run each one as an independent test. To add more tests, add more methods, each with a different name, but all starting with test....

The main method isn't needed if you are using the lab installation of JUnit. It allows you to run the tests by typing java ExampleTest instead of java junit.textui.TestRunner ExampleTest. It is polite to add this method even if you don't use it, in case anyone else is going to use your testing file.

In some older documentation, the test methods are named individually in creating the test suite. You can still do that if you want, but the newer convention illustrated here seems easier.

Shared Code

To share example objects between tests, create global variables in the ExampleTest class:

public class ExampleTest extends TestCase
{  
   Example example = new Example();

   public void testTail()
   {
      String s = example.tail("steal");
      assertEquals("teal", s);
   }

   public void testOneChar()
   {
      String s = example.tail("x");
      assertEquals("", s);
   }
   ...
}

The trouble is you can only do one-line initialising of global variables. If you want more complex initialisation code than that, shared between tests, you can define supporting methods (not starting with test...):

   Example example;

   void init()
   {
      example = new Example();
   }

   public void testTail()
   {
      init();
      ...
   }

   public void testOneChar()
   {
      init();
      ...
   }

The trouble with this is that every test method has to start with code to call your init method. To avoid that, JUnit recognises a particular method called setUp which it automatically calls at the start of every test:

   Example example;

   public void setUp()
   {
      example = new Example();
   }

   public void testTail()
   {
      ...
   }

   public void testOneChar()
   {
      ...
   }

Some sources show setUp declared as protected. That also works, but public is more consistent with everything else about using JUnit.

Assertions

In each test, you can make assertions about what the results ought to be. These are what JUnit uses to decide if a test succeeds or not. To see the range of assertion methods you can call, look at the API documentation for the junit.framework.Assert class.

This is one place where some of the tutorial documentation is out of date. In some places, they say you can use assert(), but the word assert has become a Java keyword, so you now have to use assertTrue() instead. Also, in some places it says you have to write Assert.assertTrue() but you no longer have to do that (because the TestCase class extends the Assert class). The main methods are:

assertTrue(b)                     b is any boolean expression
assertEquals(expected, actual)    compare two things of the same type
assertEquals(e, a, margin)        compare two doubles within a given margin
fail()                            tell JUnit the test has failed

Most of these have versions which start with an extra string argument, to customise the message that JUnit produces (which is especially useful with fail), and versions with the test reversed. With assertEquals, you must put the expected value first, as with:

assertEquals("teal", s);

Otherwise, JUnit's messages about failed tests don't make any sense.

Exceptions

There is one particularly tricky case. Suppose you want to assert that a particular call should cause an exception:

   public void testEmpty()
   {
      String s = example.tail("");
   }

You expect some kind of exception from this, because the tail method is supposed to remove one character from the string, and there aren't any. (In fact it if you try it, it causes a StringIndexOutOfBoundsException exception.)

The solution is to put a fail call just afterwards, and then put a try...catch block round the pair:

   public void testEmpty()
   {
      try
      {
         String s = example.tail("");
         fail("tail hasn't caused an exception when it should");
      }
      catch (Exception e)
      {
      }
   }

If the call to tail correctly causes an exception, then control jumps to the catch block, missing out the call to fail, and JUnit treats the test as a success. If tail doesn't cause an exception, then fail is called and JUnit reports a failed test. The catch block is written here to catch all exceptions, on the basis that the exact form of the exception might change as the code is developed, but that any exception is acceptable in this case.

Footnote: Why Global Environment Variables are Terrible


Ian Holyer

© 1995-2009 University of Bristol  |  Terms and Conditions
About this Page