Laboratory: Unit Testing
CSC 207 Algorithms and Object Oriented Design Spring 2011

Summary: In this lab, you will set up your unit testing environment and practice writing unit tests for our tester library.

Preparation

  1. Create a directory for this lab:
      mkdir somewhere/testing
  2. Go to your new directory:
      cd somewhere/testing
  3. Copy the files for this lab to your new directory:
    cp ~weinman/public_html/courses/CSC207/2011S/labs/code/testing/tester.jar .
    cp ~weinman/public_html/courses/CSC207/2011S/labs/code/testing/IntegerBag.class .
          
  4. The routines for our testing environment are all packaged together in the Java ARchive tester.jar that you just copied. In order to use them, you will need to tell Java where they live. You can either do every time you compile and run a test suite (which can get quite tedious), or you can do it just once, when your shell environment is configured. I prefer the latter approach, which you can do with the following steps.
    1. Open the file ~/.bashrc in your favorite text editor
    2. Somewhere near the end, but before any final export commands, add the following line:
      CLASSPATH=.:somewhere/testing/tester.jar
      where somewhere is the directory where you created a place for this lab. This command tells the JDK to look for classes in whatever the current directory is ("."), but also in the jar file.
    3. If there is already a line with an export command near the end, add the environment variable CLASSPATH to the list. In other words, there may be other variables listed, but be sure after the declarations listed in step 2, you have a line like the following:
      export PATH EDITOR VISUAL CLASSPATH
    4. Make sure these changes take effect in your current shell by typing the following at the command line:
        $ source ~/.bashrc
      They won't take effect globally until you log in again.
    5. You can verify that the environment variable was added by echoing it at the command line:
      $ echo $CLASSPATH
  5. Open the Tester Overview page in a new browser tab for handy reference.
  6. We'll also need to configure JDE to find the tester class.
    1. Open your .emacs file for editing:
        $ emacs ~/.emacs &
    2. Find the line that looks like this:
      (setq jde-global-classpath (quote (".")))
    3. Note that you put the tester JAR in somewhere/tester, and you decided where somewhere is. You need to add this to your class path, by changing above line in your ~/.emacs to read instead:
      (setq jde-global-classpath (quote ("." "/home/username/somewhere/testing/tester.jar")))
      being sure that you replace somewhere with the appropriate directory you put things in. (Include the leading slash to make the directory absolute, rather than relative.)

Exercises

Exercise 1

One of the files you copied is a class for doing some simple operations on a bag (or set) of integer numbers. Read the JavaDoc for IntegerBag to understand what the objects of the class are to do.

Exercise 2

Create a class called TestIntegerBag in the same directory as the IntegerBag.class file. Import the all of the classes from the tester package to your class:
import tester.*;

Exercise 3

Think about what sorts of things you may want to test about the routines in IntegerBag. What behaviors does the documentation specify? What sorts of input can you create to ensure correct functionality?

Add a variety of IntegerBag objects as member variables of your test class. (Note: you do not need to do this in a constructor. Also, you can create and populate arrays on the fly with new Integer[]{1,2,3} .)

Exercise 4

Add an object method to your class for testing the size() operation. Remember that Tester requires the test methods to begin with the name test. For instance:
  IntegerBag empty = new IntegerBag(null);

  // ...

  public void testSize(Tester t)
  {
    /* Check size of empty bag */
    t.checkExpect(empty.size(), 0, "size: Null/empty bag");

    // more tests ...
  }
When you are finished, make sure your class compiles.

Exercise 5

The typical way to check your tests is by including a main driver method in your test object class. Thus, you will need to do the following:
  1. Add a main method.
  2. Create an instance of TestIntegerBag inside your driver.
  3. The Tester class features the following method:
    public static void runReport(java.lang.Object obj,
                                 boolean full,
                                 boolean printall)
    This is your hook to run the tester for any object and produce a test report. Your instance of TestIntegerBag should be the first argument You can use the other to options to select whether to print all tests and/or data from all results.

    Add a call to this method and run your tests.

  4. Experiment with the options full and printAll to see what the settings do (and which you find most useful as a default).
  5. You should find one category of error in size. If you haven't detected it yet, re-read the constructor documentation carefully.
  6. If you still haven't detected it, think about why the class takes an array of reference types, rather than an array of primitive types.

Exercise 6

  1. The max method may be incorrect. Develop a comprehensive suite of tests in a method called testMax to see if you can uncover bugs in the implementation.
  2. Compile your test class and run your tests.
  3. The method product may also be incorrect. Develop a comprehensive suite of tests in a method called testProduct to see if you can uncover bugs in the implementation.
  4. Compile your test class and run your tests.

Exercise 7

One of the things you may have (should have!) found was exceptions being thrown that you didn't expect. Due to the architecture of the tester, an exception caused by your checks results in terminating the test method you are in, so that no more checks in that method are run. While other test methods will be run, you would have to fix the bug (exception) in order to continue checks in that method.

You may proceed here by commenting out and documenting any checks that give you unexpected exceptions so that you can continue with the other checks in the test method.

Exercise 8

Conversely to the above situation, testing whether advertised bad behavior actually occurs is also often important.

For this reason, the tester library also allows us to check whether we actually get an exception when it is expected. Not only that, it will report whether the exception was of the right type, and also had the expected message with a method like the following

public boolean checkException(java.lang.String testname,
                                  java.lang.Exception e,
                                  java.lang.Object object,
                                  java.lang.String method,
                                  java.lang.Object... args)
Thus, to test whether the call to min fails with our empty bag of numbers, as it should, we might write
checkException("Min: null empty bag",
               new IllegalStateException(), // No exception message (tester cares)
               empty,                       // Object we are testing
               "min");                      // Method to test
Note that the args argument (used with ellipses because they are optional and variable in number) are omitted since min takes no parameters.

  1. Try this test and verify that it works.
  2. What happens if you add a message to the expected exception? I.e., new IllegalStateException("Busted!");
  3. Test other cases of the advertised exceptions in the class. They may be broken.
Created: Jerod Weinman, 21 January 2010
Revised: Jerod Weinman, 11 February 2011
Copyright © 2009-2011 Jerod Weinman.
CC-BY-NC-SA
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License .