JUnit Testing Tips
Fall 2007, David Matuszek

Basics

Scope is important (as always). If you are testing the Transmogrifier class, and you need a transmogrifier in most of your methods, you would begin by declaring, but not initializing, a transmogrifier as an instance variable:

     class TransmogrifierTest {
         Transmogrifier tm;

(It's usually best to give this instance variable a short name, because you will be using it a lot.) The reason for not giving it an initial value when you declare it is that this initialization happens only once. You will typically need a brand new, unspoiled object for each and every test; to make this happen, initialize your variable in the setUp() method.

     public void setUp() {
         tm = new Transmogrifier("fiddle", 3);
     }

You can use this pattern for any variables you use in multiple tests. Of course, if you only use a variable in one or two tests, you can always declare it and initialize it in just those tests, if that makes the tests more readable.

Your JUnit tests will test whether your methods return the answers you expect. That means you need to know what the correct answers are. When you set up your tests, don't use random numbers, or input from a file, or other things you can't completely control. If you are testing addition, test whether 2 plus 3 equals 5; don't test whether 85674 plus 36627 equals 123301.

The two most important JUnit methods are these:
      assertEquals(expectedValue, methodCallToGetActualValue);
and
     assertTrue(condition);

Of these, assertEquals is more useful, because if the assertion fails, you can find out what value was actually returned. With assertTrue, you only know that the condition was false.

You can add a message as a first parameter to any assertion method, in order to provide additional information. For example,

     assertTrue("x = " + x, 0 <= x && x <= 100);

What tests should I write first?

Typically, methods in a class use other methods in the class. If method one calls method two, you can't really write and test method one until you know method two is working, so you should start with method two.

In general, look first for the simplest methods, and methods that don't depend on other methods. Then build up gradually, writing and testing methods that depend only on methods you already have (and that you know work). Sometimes static methods are a good place to start, because they are independent of any objects of the class.

By working "from the bottom up," you can make one method at a time "go green" (pass its JUnit test). You make slow but steady progress toward a complete, working program. While there are no guarantees, this approach makes it much less likely that you will be stuck for hours trying to find an elusive bug.

How can I test a constructor?

A constructor returns a new object. Almost always, at least some information about the object is available to "the outside world" (other classes), otherwise there's hardly any point in having the object. You can only test what you have access to.

Sometimes a new object is so "empty" that there's hardly anything meaningful you can ask it until you have used some of the methods of the object. In that case, testing the other methods may be enough.

How can I test methods independently of one another?

We try to make methods relatively standalone and independent of one another, but it doesn't always work out. For example, if you have getter and setter methods for a variable, it's pretty difficult to test whether the setter works without using the getter method to see what it did. In fact, you might want to combine the tests for these into a testGetAndSetValue method.

When testing a method, use any other methods you need. Just don't use methods you don't need.

How can I test a private method?

Private methods can only be tested indirectly, by testing other methods that use the private method.

How can I test a void method?

Some void methods just do output, and have no effect on the future execution of the program. It is possible to capture the output and make sure it is correct, but for now we are just ignoring such methods.

Other void methods modify the state of the object in some way. You can test the state, to see if it has been modified in the way you expected. Sometimes you can ask the object directly about its state. Other times, you may need more indirect tests, to see if the object's behavior changes appropriately in response to the change in its state.

How do I test a method that uses random numbers?

As always, figure out what you expect, and look for that.

If you are testing a six-sided die, try rolling it six hundred times, and make sure each number comes up at least once.

If you are shuffling an array of integers, you want to make sure the result has the same numbers in it. One quick way to do this is to add up the numbers both before and after shuffling; the totals should be the same. You also want to check that the array isn't exactly the same as when you started.

How do I test a method that returns an array?

assertEquals doesn't work for arrays. However, the Arrays class has a static method Arrays.equals(oneArray, theOtherArray), for one-dimensional arrays. For two-dimensional arrays or greater, use the method Arrays.deepEquals(oneArray, theOtherArray).

How do I test that my method throws an Exception?

Sometimes you want your methods to throw exceptions. For example, suppose you have a method setScore(int score) in the Exam class, and you want the method to throw an IllegalArgumentException if the score isn't in the range 0 to 100. Here's how you would test that:

     try {
         exam.setScore(-1);
         fail ();
     catch (IllegalArgumentException e) { }
     try {
         exam.setScore(101);
         fail ();
     catch (IllegalArgumentException e) { }

My tests do the same things over and over!

There is often a lot of repetition in tests. For example,

     assertEquals("Saturday", d.findDayOfWeek(2000, 1, 1));
     assertEquals("Tuesday", d.findDayOfWeek(2000, 2, 1));
     assertEquals("Wednesday", d.findDayOfWeek(2000, 3, 1));

This is a case where copying and pasting is probably a good idea--at least, when each line is a new test. However, if you are doing computations in your JUnit tests, you should pay attention to the DRY principle, and do the common work in a method. For example,

     ocean.placeCruiserAt(4, 6, true);
     assertEquals(count + CRUISER_LENGTH, numberOfSquaresOccupied());
     ...
     ocean.placeDestroyerAt(9, 6, true);
     assertEquals(count + DESTROYER_LENGTH, numberOfSquaresOccupied());

In the above example, numberOfSquaresOccupied is a private method used in several places.

I can't find the error in my method

Maybe the test is wrong.

JUnit tests whether a method does what you expected, or returned the result you expected. Usually, this is a result you worked out manually.

JUnit isn't running my test!

Your test method has to be public, its name has to begin with the letters "test", and it has to have an empty parameter list.