Comp201: Principles of Object-Oriented Programming I
Spring 2008 -- Lab02: Unit Tests, Writing Classes and Making Interacting Objects     


I. Writing a simple class in DrJava (45 min)


First steps:

  1. If you have not done it already, download the latest DrJavato your Desktop.  Use this link!
  2. Create a Comp201 directory in your U: drive (or My Documents--same place)
  3. Create a "lab02" directory in your Comp201 directory.   Do the same sort of thing when doing homework, etc.
  4. The following is due to a bug in the current DrJava and will not be necessary in the future:
    1. Copy your entire Comp201 directory to the C:\temp directory -- DrJava will not run properly when the files are saved to your U: drive.
    2. At the end of lab, copy your Comp201\lab02 directory back to your U: drive and delete it off of the C: drive!
  5. Set your DrJava "Language Level" to "Elementary"  (If you are already familiar with Java syntax, go ahead and use "Full Java", but the directions will be specific for Elementary level.)

Creating a class to calculate rectangle areas in DrJava

We begin the process of writing a class that knows how to compute the area of any rectangle given a width and a height, as discussed in lecture #2.  We will write the class in several small steps.  At each step, we will compile the code to ensure that everything is syntactically correct.  By compiling the code at each small step, we hope to avoid seeing a large number of error messages that can be rather intimidating.

  1. In the Definitions pane (upper right pane), type the following:
    class AreaCalc { 
    }

    Note the placement of the curly braces: the opening brace is on the same line as the class name, while the closing brace lines up with the beginning of the class definition on a new line.  This is the de-facto Java coding style.

    This class is not very interesting because we cannot do a whole lot with it.  However, it is syntactically correct.  So let's save and compile it.
     

  2. Save the file in your Comp201\lab02 folder.    The file will be saved as  a ".dj0" file, which is a special Elementary level file. 
      
  3. Click the "Compile All" button in DrJava.    If all is well, the file should compile with no errors.
     
  4. In the Interactions pane (bottom pane), type in the following:
    AreaCalc calc = new AreaCalc();
    This defines a variable called "calc" of type AreaCalc and initializes it to refer to a new object made by your AreaCalc class definition.
     
  5. There isn't much you can do with this class yet, but that doesn't mean that it is non-interacting.   In the Interactions pane, type "calc" and hit Enter.     This action will call the toString method of the object referred to by calc.   All objects in Java have a toString method and this one is no exception.   What you will get back is the current String representation of the object.    Elementary language level will return something like "AreaCalc ()" while if you wrote in Full Java language level it will return something more cryptic.
     
  6. Take a look at your Comp201\lab02 directory and you will see several new files.
    1. The compiler automatically created a file called AreaCalc .class that is the compiled code, now in "Java bytecode", ready for the Java Virtual Machine.
    2. DrJava's Elementary language level has automatically generated the "real" Java file, AreaCalc .java which has all the full-blown Java code in it that we don't want you worrying about at this stage.   Feel free to open the .java file in DrJava.    Close it when you are done so as not to confuse anything.

    The purpose of this step is to give you some idea of what DrJava is doing "behind the scene".  You do not have to concern yourself with all the code that is automatically generated at all.
     

  7. Now you are ready to add to AreaCalc a method to compute the area of a rectangle.  Edit the above AreaCalc class as follows:
    class AreaCalc { 
        double rectArea(double width, double height) {
            return width * height;
        }
    }
  8. Compile All: the AreaCalc file is automatically saved.
     
  9. To create a new instance of an AreaCalc , type the following in the Interactions pane:
    AreaCalc calc = new AreaCalc();
  10. To "ask" the AreaCalc object calc to compute the area of a 4 by 5 rectangle, type in
    calc.rectArea(4, 5)
  11. Experiment with using the object calc to compute the area of a few rectangles of different sizes.
     
  12. How do you instantiate another AreaCalc and name it robot?

Computing the area of a right triangle

Suppose you want to compute the area of a right triangle given its base and its height.  What code should you write?  You basically have two options.

  1. Write another class called AreaCalc2 with a method to compute the area of a right triangle given as input parameters the base and the height.
     
  2. Add a method to class AreaCalc to compute the area of a right triangle given as input parameters the base and the height.

Suppose now you are asked to compute the area of a circle.  What approach would you choose?

Creating classes with fields

The following are classes with fields similar to class Person discussed in lecture #03.

  1. Write a class called Rectangle that has two double fields, width and height, and a method to compute the area.
     
  2. Write a class called RightTriangle that has two double fields, base and height, and a method to compute the area.
     
  3. Write a class called Circle that has one double field, radius, and a method to compute the area.

 


II. Unit Testing with  JUnit (35 min)

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.

Step 0. 

Run DrJava with Elementary Language level.  We will do all development in DrJava since it has very nicely integrated JUnit with its development environment.

Step 1. 

As discussed in lecture #03, 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.  

class Person {

    /**
    * Computes the number of months till the next birthday given the current month.
    */
    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(...)

Step 2. 

DrJava is very nice to you and will create a test stub class for you if you know what to click: 

  • Go to the File menu and select New Junit Test Case...
  • Create a stub test case called Test_Person.  DrJava will create a stub class that looks like the following
     
    /**
     * A JUnit test case class.
     * Every method starting with the word "test" will be called when running
     * the test with JUnit.
     */
    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.)
         */
        void testX() {
        }
    }

    At this point, do not worry about what "extends TestCase" means in the above.  DrJava is re-using the class TestCase from the JUnit framework in some clever way without you having to deal with all the details of how to use an existing class from some other files.

     

  • Compile this test class only (by clicking on Tools/Compile current document), and save the class in the same directory as Person.  The Test_Person class should compile, though the Person class does not.
     
  • 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.

class Test_Person extends TestCase {

    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 have decided that we only need to know the birth month of a person in order to compute the number of months till the next birth day.  As a result, we instantiate a Person object by passing only the birth month: new Person(9). 

Also the test covers three cases:

  • the current month is less than the birth month 
  • the current month is equal to the birth month
  • the current month is greater than the birth month.

The assertEquals method comes from the class TestCase and takes in three parameters:

  • the first parameter is a String
  • the second parameter is the expected result
  • the third parameter is the actual result of the computation you are testing.

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.

Step 3.

Fix the code for Person  until Test_Person compiles!  (See the code in blue below.)

First add a int field called _bMonth and compile.  What happens?

Now add the statement return 0; to the body of the method nMonthTillBD(...) and compile.

class Person {
    int _bMonth;

    /**
    * Computes the number of months till the next birthday.
    */
    public int nMonthTillBD(int currentMonth) {
        return 0; // to do
    }
}

Step 4.

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.

Step 5.1

Change the formula to

    public int nMonthTillBD(int currentMonth) {
        return _bMonth - currentMonth; // to do
    }

Compile all and test again.

Still we have errors.

Step 5.2

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.

Additional Reading:

 

 

 


Last Revised Thursday, 03-Jun-2010 09:50:27 CDT

©2008 Stephen Wong and Dung Nguyen