This tutorial covers:
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.
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.
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 stubbed 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(...).
To write test code using the JUnit framework, in the simplest case all we have to do is:
Here, Assert is a class from the package junit.framework.
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.
assertTrue("peter is
null!", !peter.equals(null));
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:
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 Person to make the test code compile.
Fix the code for Person until Test_Person compiles!
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
}
}
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.
Change the formula to
public int nMonthTillBD(int currentMonth) {
return _bMonth - currentMonth; // to do
}
Compile all and test again.
Still we have errors.
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.
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:
jar xf listSource.jar
Section III below will provide a more detailed discussion of the JDK's JAR utility.
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?
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.
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:
Add the package scheme;
declaration to the top of IList.java
.
You need to create a subdirectory called scheme
and
move IList.java
into it.
Now compile using
javac scheme/IList.javaNote that 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.
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 lab. 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.
In the following exercises, assume NEList contains Integer objects only. Integer is called the wrapper class for int. You will need to use its intValue() method and its constructor Integer(int i). The detailed documentation for Integer can be found at: http://java.sun.com/j2se/1.4/docs/api/java/lang/Integer.html.
Add the following methods to IList
and its
appropriate subclasses as specified.
IList
.
Add the method test_sum to the Test_List class.You will probably not have enough to do all of the above exercise. Continue working on them off line.