Incremental Java
Testing a Class, Part 2

How to Test a Class

Suppose you have a class called Foo and either plan to write this class and want to write a test class, beforehand, so you learn the principles of test-driven development. Or the class has already been written, and you want to make sure to remove bugs. Or someone else may have written the class, and you want to learn more about it.

Whatever reason you have for testing a class, one way to do this is to create a special test class. Recall that a test class is not a special Java class. If you look up a reference book on Java, you won't see "test classes". It's a class just like any other class.

The difference is how we use the class. In particular, we use the class for its main() method. Since it's difficult to come up with names for classes, you can add the word Test to the end of the class you want to test. You have class Foo, so you create FooTest.

In main() of FooTest, you declare objects of type Foo (you can declare other objects or primitive variables, if you need it in support of the tests you're writing).

If Foo has already been written, then it's easy to run your test. Just do:

% javac FooTest.java
% java FooTest
at a UNIX prompt. Foo.java needs to be in the same directory (or somewhere accessible). If Foo hasn't been compiled, javac will compile Foo, assuming it can find it (if it's in the same directory, it can find it).

The Java compiler is clever enough to check to see if there are other files that need compiling, and compiles them. Other compilers require that you explicitly type out all the programs you intend to compile. Even Java permits this:

% javac FooTest.java Foo.java
You can put two or more Java files after javac and it compiles all files on the command line. (The line above with javac is the command line).

Stubs

Let's say you're doing test-driven development. You write a test class, say FooTest, prior to writing Foo. This means that Foo doesn't already exist.

javac won't be able to compile FooTest, because it expects Foo to exist. You want to compile a class definition because it helps get rid of some of the simple syntax errors sooner, rather than later, and yet, you want to write the test class first.

How can you solve this problem? As you might guess from the name of the section, you're going to use stub methods.

One way to write a class that doesn't work very quickly is to write stub methods. Basically, you return the simplest value for return type. For example, 0 for int, 0.0 for double, and false for boolean. Maybe "" for String.

Here's how to write it for Rectangle.java, assuming you hadn't already written it.

public class Rectangle 
{
  private int width = 0, height = 0 ;

  public Rectangle() // STUB 
  {
  }

  public Rectangle( int initWidth, int initHeight ) 
  {
  }

  public void setWidth( int newWidth ) // STUB 
  {
  }

  public void setHeight( int newHeight )
  {
     return 0 ;  // STUB 
  }

  public int getWidth()
  {
     return 0 ;  // STUB 
  }

  public int getHeight()
  {
     return 0 ;  // STUB 
  }

  public int getPerimeter()
  {
     return 0 ;  // STUB 
  }

  public int getArea() 
  {
     return 0 ;  // STUB 
  }

  public boolean isSquare() 
  {
     return false ; // STUB 
  }
}
If a method has a void return type (or is a constructor), you can leave the method body blank (you always need a method body, but you can leave it blank). If it has a non-void return type, then return a simple value.

Clearly, this code does not work. But, for now, it doesn't need to. The whole point is to get it to compile. Once it compiles, you can write RectangleTest.java, and get that file to compile. Of course, if you run RectangleTest, you'll get incorrect results because Rectangle hasn't been implemented correctly.

Why Stubs Help

Do stubs sound like a silly idea? Here's how you would normally do code development using test-driven development. So you want to implement the methods one at a time, perhaps picking the easiest methods to implement first.

The reason writing tests ahead of time help is because, as a class designer, you want to think like an object user.

Ever used a VCR? Comedians used to joke about how hard it was to program a clock on a VCR. In fact, it was (and is) hard to program the clock. Why was it so difficult? Because engineers designed the VCR. They designed it to be convenient for them. They didn't test it out on everyday users who would have told them their solution didn't make sense.

This kind of concern is called usability. It used to be an obscure field of study in computer science. However, due to the Web and the huge number of programs that have become increasing complicated, the usability of these programs can mean the difference between having a successful product or not.

When you write the class first, you often pick a quick solution instead of a good solution. When you write a test first, you think about how an object user would want to use the object first, and that makes it more likely to be usable. (If you were to do this more seriously, you might have people use your class and see what they think).

Another Thing

As you finish implementing the methods, you can remove the comment that says STUB. This comment is placed so you can remember that you have a stub, and it eventually should be replaced by real Java code.