Comp 212 - Fall 2003

Lab 12: Unit Testing with JUnit


Extreme Programming (XP) is a software development methodology that is "very hot" these days and is currently embraced by the software industry.  One key element of XP, called "unit testing", is the idea of writing test code for functions (or procedures or object methods) before the functions (or procedures or object methods) are even written.  (Don't ask me how to write test code for the test code themselves!  The whole thing is "kinda" suspiciously recursive with no well-defined base cases.)  The test code becomes in effect the "documentation and specification" of the behavior of the functions (or procedures or object methods) in code form!  Each time a function (or procedure or object method) is modified, it must pass its existing test code.  Each time a test code for a function (or procedure or object method) is modified, the corresponding function (or procedure or object method) may have to be revised to satisfy the new specification.

JUnit (www.junit.org) is an open source framework developed to support the above concept of unit testing for Java programs.  This tutorial will lead you through a simple example of how to write unit test code in developing a (simple) Java program.  It is plagiarized from the example given at the link: http://junit.sourceforge.net/doc/testinfected/testing.htm.

Step 0. 

Run DrJava.  We will do all development in DrJava since it has very nicely integrated JUnit with its development environment.  You may want to peruse the online help for DrJava and read about Testing using JUnit there.

Step 1. 

Suppose we want to model the concept of money.  We think of money as something that has an amount in certain currency, and we want to compare money for equality.  For the sake of simplicity, we model the amount as int and the currency as String.  We start by writing stubbed code for the class Money that is to represent our concept of money.  

Aside: as far as I understand, XP does not talk about interfaces and abstract classes.

public class Money {

    public int amount() {
        // to do
    }

    public String currency() {
        // to do
    }

}

Notice in the above there is really no concrete code.  As a matter of fact, the above would not compile.  We are interested in equality, so we need to have a method for comparing for equality.  We can simply use the equals method inherited from objects and do not need to write the stub code for equals at all.  Now we must abandon everything and start writing test code for amount(), currency() and equals(...).  To keep the discussion short, we will ignore in our subsequent discussion test code for amount() and currency(), which are trivial.

Step 2. 

To write test code using the JUnit framework, in the simplest case all we have to do is:

DrJava is very nice to you and will create the above stub class for you if you know what to click: 

import junit.framework.*;

public class MoneyTest extends TestCase {

    public void testEquals() {
        Money m12CHF= new Money(12, "CHF");
        Money m14CHF= new Money(14, "CHF");

        Assert.assertTrue(!m12CHF.equals(null));
        Assert.assertEquals(m12CHF, m12CHF);
        Assert.assertEquals(m12CHF, new Money(12, "CHF")); 
        Assert.assertTrue(!m12CHF.equals(m14CHF));
    }
}

Note that in the code for testEquals(), we arbitrarily decide that we must have a constructor for Money as shown in the code in order to instantiate any concrete Money object.  When you compile the above code in DrJava, clearly it won't compile.  So you will have to go in and fix the code for Money to make the test code compile.

Step 3.

Fix the code for Money until MoneyTest compiles!

public class Money {
    private int fAmount;
    private String fCurrency;
    public Money(int amount, String currency) {
        fAmount= amount;
        fCurrency= currency;
    }

    public int amount() {
        return fAmount;
    }

    public String currency() {
        return fCurrency;
    }
}

Step 4.

After you have cleaned up your code for Money as shown in the above, you should be able to compile MoneyTest.  With MoneyTest open, click on the Test button in DrJava tool bar.

What do you see?  Something has failed!  The inherited equals method seems to be the culprit.  Fix it!

Step 5.

Override the equals method in class Money to pass the MoneyTest testEquals() method.

public boolean equals(Object anObject) {
    if (anObject instanceof Money) {
        Money aMoney= (Money)anObject;
        return aMoney.currency().equals(currency())
                && amount() == aMoney.amount();
    }
    return false;
}

When you add the above method to Money, your MoneyTest code should compile, run and pass with no error messages!

In XP programming, only after a method has passed its unit test that you are allowed to proceed to another one.

Exercises

1. Apply the above unit test methodology  to add two Money objects and get a new Money object.

2. Now you can study the example shown at the link http://junit.sourceforge.net/doc/testinfected/testing.htm, and complete the rest of the exercise there.

3. Suppose you are given the abstract specification of the immutable list framework, listFW, given in http://www.owlnet.rice.edu/~comp212/03-fall/ds/listFW/doc/.  Imagine you are to implement the concrete class CompositeListFactory yourself from scratch.  Develop a JUnit test suite for your CompositeListFactory by applying the XP testing methodology illustrated in the above Money example.  Note: this is an elaborate exercise that will require more than the allotted lab time to complete.

 


dxnguyen@cs.rice.edu
revised 11/10/03