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.
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(...).
To write test code using the JUnit framework, in the simplest case all we have to do is:
Here,
DrJava is very nice to you and will create the above stub class for you if you know what to click:
import junit.framework.TestCase;
/** * A JUnit test case class. * Every method starting with the word "test" will be called when running * the test with JUnit. */ public class Test_Person extends TestCase {
/** * A test method. * (Replace "X" with a name describing the test. You may write as * many "testSomething" methods in this class as you wish, and each * one will be called when running JUnit over this class.) */ public void testX() { } }
Change the name of the stub method testX() in the above to test_nMonthTillBD()and add code to make it look like the code below.
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:
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.
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.
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?
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?
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
.
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.
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.
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
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.