Comp 212 Lab 2: JUnit, JavaDoc, Package


Introduction

This tutorial covers:


I. Unit Testing with  JUnit (30 min)

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 with only one class.

Step 0. 

Run DrJava.  We will do all development in DrJava since it has very nicely integrated JUnit with its development environment.

Step 1. 

Suppose we want to model the notion of a smart person who knows his/her birthday and can compute the number of months till his/her next birthday given the current month.  For our purpose, a month can simply be represented by an integer, which in Java  is called int..  We start by writing "stub" code for the class Person  that is to represent our smart person.  

public class Person {

    /**
    * Computes the number of months till the next birthday.
    */
    public int nMonthTillBD(int currentMonth) {
        // to do
    }

}

Notice in the above there is really no concrete code.  As a matter of fact, the above would not compile.  Now we must abandon everything and start writing test code for nMonthTillBD(...)

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 Test_Person extends TestCase {

    public void test_nMonthTillBD() {

        Person peter = new Person(9); // a person born in September.

        assertEquals("Calling nMonthTillBD(2).", 7, peter.nMonthTillBD(2));
        assertEquals("Calling nMonthTillBD(9).", 0, peter.nMonthTillBD(9));
        assertEquals("Calling nMonthTillBD(12).", 9, peter.nMonthTillBD(12));
    }

}

Note that in the code for test_nMonthTillBD(), we arbitrarily decide that we must have a constructor for Person as shown in the code in order to instantiate any concrete Person object to be born with a given birth month.  Also the test covers three cases:

The assertEquals method comes from the class TestCase and takes in three parameters:
  • the first parameter is a String
  • the second parameter is the expected result
  • the third parameter is the actual result of the computation you are testing.

Most of the time, your test code will call on the assertEquals method to test for equality between the result of the computation you are testing and the expected result.

When you compile the above code in DrJava, it won't compile.  You will have to go in and fix the code for Person to make the test code compile.

Step 3.

Fix the code for Person  until Test_Person compiles!

(See the code in blue below.)
  • First add a int field called _bMonth and compile.  What happens?
  • Add a constructor for Person that takes in an int representing the birth month as input.  Compile.
  • Now add the statement return 0; to the body of the method nMonthTillBD(...) and compile.

public class Person {
    private int _bMonth;

    public Person(int birthMonth) {
        _bMonth= birthMonth;
    }


    /**
    * Computes the number of months till the next birthday.
    */
    public int nMonthTillBD(int currentMonth) {
        return 0; // to do
    }
}

Step 4.

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

What do you see?  Something has failed!  The formula for the number of months till the next birth day seems to be the culprit.  We will pretend ignorance and fix the "bug" in two steps.

Step 5.1

Change the formula to

    public int nMonthTillBD(int currentMonth) {
        return _bMonth - currentMonth; // to do
    }

Compile all and test again.

Still we have errors.

Step 5.2

Change the formula to

    public int nMonthTillBD(int currentMonth) {
        return (_bMonth - currentMonth + 12) % 12; // to do
    }

Compile all and test again.  Now everything should pass!  You may now remove the TO DO comment from the code of nMonthTillBD.

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

Step 6: Food for thought

What if we want the Person class to be able to compute the number of days until the next birthday?

Additional Reading:

 

2. Java Documentation Style (15 min)

The Java Development Kit (JDK) comes with a tool called JavaDoc.  This tool will generate documentation for Java source code with comments written in accordance with the Java documentation style.   The following links show more examples.  You do need to spend time outside of the lab to study them.

Click here to download a single jar file (listSource.jar) containing sample Java code into your own directory.  It includes Java source code for a version of lists discussed in the past few lectures.  Extract the files from the jar file using the command (in a command window):

jar -xfv listSource.jar

Note: if your (Windows) system does not set the full class path to the java jdk installation, then you will need to enter the full path name for the jar program.  For example:

c:\"Program Files"\Java\jdk1.5.0\bin\jar -xf listSource.jar

We will provide a more detailed discussion of the JDK's JAR utility in another lab.

Use DrJava to look at both the code and the comments, which follow the Javadoc conventions.  The following is a very short summary of the Javadoc conventions.

Creating javadoc using DrJava:

In DrJava, 

Now change the javadoc Access level in the javadoc Preferences to private and generate javadoc again.  What is  the difference?

Creating javadoc (1.1 version) using StructureBuilder

You can generate javadoc using the Tools/Generate Documentation menu in StructureBuilder.  What you get is the UML class diagram diagram together with the javadoc (version 1.1 style).  You may use this tool to generate javadoc and UML diagrams for all of the assignments.

 


III. Package (25 min)

A Java package is a grouping of classes similar to the notion of a directory is a grouping of files. Packages are used to help avoid name clashes and to hide particular pieces of code from the clients. A package has a name, such as utility or java.lang.util. In general, a package name is a series of strings of alphanumeric characters (starting with an alphabetic character) and separated by periods. To make a java class part of a particular package, say scheme, you must add the declaration package scheme; to the very top of the class source file.

Also, you will need to put the file in the directory structure that mirrors the package name. For example, the java classes that belong to the package scheme should be in a directory also named scheme. If you don't do this, it will still compile, but it won't run correctly.

Exercises:

  1. Add the package scheme; declaration to the top of IList.java.  Compile it using DrJava Tools/Compile Current document.  You should get an error message saying that you are in the wrong package.  Close the file for now.

    You need to create a subdirectory called scheme and move IList.java into it. 

    Reopen the file in the scheme subdirectory . Now compile again.  You should get no error this time.

    Note: if you use the command window to compile with the command javac, you should always compile from your project's main directory. If you compile from within a package subdirectory, it doesn't find all the supporting definitions.

    We can't run anything yet, because that's just a piece of the whole program.
     

  2. Add the package scheme; declaration to the top of IEmptyList.java, INEList.java,  EmptyList.java , NEList.java and ListOps.java, and move them into the scheme subdirectory. Do not make Test_List.java part of the package. Test_List.java  does not have a package name, and is thus said to be in the no-name (or default) package.  

    Also, remove the public access from the EmptyList and NEList classes. By default, a class is "package-private", i.e., it is known within the package, but not from outside. If you try to compile Test_List.java now, you will get an error message. Try it to see what happens.

    You need to add the statement import scheme.*; to the top of Test_List.java  to indicate to the compiler that you are using all the public classes in that package. Try to compile it again.

    You should see a few error messages saying that you can't use EmptyList.java and NEList.java because these classes are not public. This is because the Test_List class is not part of the scheme package. One way to resolve this problem by making Test_List part of the scheme package. A class of a package can access all the classes (public or "package-private") in the package. However this is not a good solution in general because a client may be using many classes from different packages, but not class can be part of more than one package. A better solution is to use a "factory".  We will discuss factories in another lecture.  For now, just make EmptyList.java and NEList.java public again, and recompile Test_List.java . You should get no error.  Try to run Test_List.java  now by click the Test button in DrJava.


IV. Using an existing class: Calendar (15 min)

The purpose of this exercise is to learn how to use existing classes to perform certain useful tasks.  Basically, it entails reading the documentation, called the API, and try out calling a few methods using DrJava.  The classes we choose are Calendar and GregorianCalendar.  They will prove to be useful for solving the problem of computing teh number of days till the next birthday as stated in step 6 of

  1. Go to the course main web page, find and click the link to the Java 1.5 API Web Site (in the Resources links section).
  2. From this page find and click the link to the class Calendar in the All Classes pane on the left hand side.  You should see the documentation for Calendar.

Calendar is an abstract class used to represent and perform common computations on date and time.  The people who develop Java have written the code to carry out most of the low-level computations, but have left a few methods "abstract" (i.e. with no code body) for the user to inherit and implement.  This way, you can re-use the existing code without having to know anything about them.  You will learn more about abstract class in the next lecture.

The Java developers also have provided us with a concrete subclass of Calendar called GregorianCalendar to represent the standard calendar that we (and most of the world) are using these days.  At the top level description of Calendar, you should see a description of direct known subclasses and the class GregorianCalendar listed there.  Click on this link to see the documentation for GregorianCalendar.

Use  the Interactions pane of DrJava to explore the Calendar and GregorianCalendar classes.

To use the above classes, you must enter the following statement:

To instantiate a GregorianCalendar object containing today's date, enter

Now enter:

What do you see?

Enter:

Can you see what it doing?

Here is how you create a GregorianCalendar object that corresponds to March 03, 2000:

Now you can check the time, year, month and date of mar03_02 to see if it contains the information that you have wanted to record.

Try these also:

The only way you deal with a Calendar object is to call on the public methods or to access the public fields (such as Calendar.YEAR).  To paraphrase a famous quote: Ask not what you can do with an Object, ask what the Object can do for you!

Back to step 6 of Section I: use the class Calendar and GregorianCalendar to

You will probably not have enough to do all of the above exercise.  Continue working on them off line.


 
Prepared by Dũng X. Nguyễn  
last revised 01/24/2005