Class Under Test
We begin with our class under test, CompositeDate.
CompositeDate is a simple-minded class that represents
a date as a tuple consisting of a year, month and day of the month.
Of course, it isn't intended for any production use, but it
has some interesting properties for testing, because,
as we know, there are many boundary conditions and constraints.
CompositeDate imposes preconditions on its clients, namely,
that months are integers in the range from 1 to 12, inclusive,
and days are integers from 1 to 31.
There are many combinations of year, month and day that
are invalid, but that satisfy these preconditions.
CompositeDate checks for these cases and throws an
InvalidDateException whenever these are detected.
The task of testing a class like this quickly turns to
the identification of boundary and special cases, and
then proving that these are all handled correctly.
CompositeDate includes three types of state-changing methods
that require testing: the constructor, field setters, and
field incrementers and decrementers.
For the purpose of this exercise, only tests
of the constructor will be shown; it should be
obvious to the reader that the patterns of test
design would apply equally to tests of the other methods.
Here is an excerpt from our class under test, CompositeDate.
package org.springunit.examples;
public class CompositeDate implements Serializable {
/**
* Create CompositeDate having day, month and year.
* @Pre("1 <= day && day <= 31")
* @Pre("1 <= month && month <= 12")
* @throws InvalidDateException if day, month and year do
* not specify a valid date
*/
public CompositeDate(int year, int month, int day) throws InvalidDateException {
assert 1 <= day && day <= 31 : "1 <= day && day <= 31";
assert 1 <= month && month <= 12 : "1 <= month && month <= 12";
this.day = day;
this.month = month;
this.year = year;
validateDate(year, month, day);
}
public static boolean isValidDate(int year, int month, int day) {
return isValidLeapDay(year, month, day) || isValidMonthAndDay(month, day);
}
public static boolean isValidLeapDay(int year, int month, int day) {
return (day != 29 || month != 2) || ((year % 400 == 0) || (year % 4 == 0 && year % 100 != 0));
}
public static boolean isValidMonthAndDay(int month, int day) {
assert 1 <= month && month <= 12 : "1 <= month && month <= 12";
assert 1 <= day && day <= 31 : "1 <= day && day <= 31";
return !((day > 29 && month == 2) || (day > 30 && (month == 4 || month == 6 || month == 9 || month == 11)));
}
protected static void validateDate(int year, int month, int day) throws InvalidDateException {
validateLeapDay(year, month, day);
validateMonthAndDay(month, day);
}
protected static void validateLeapDay(int year, int month, int day) throws InvalidDateException {
if (!isValidLeapDay(year, month, day)) {
throw new InvalidDateException("February 29 is not valid for the year " + year);
}
}
protected static void validateMonthAndDay(int month, int day) throws InvalidDateException {
if (!isValidMonthAndDay(month, day)) {
throw new InvalidDateException(day + " is not valid for the month " + month);
}
}
private int day;
private int month;
private int year;
}