1B1b Programming Exercises 3

Aim to Finish by Tuesday 26th February 2004


Purpose: Testing programs.

Goal: Complete as many of the exercise questions as you can. If you are keeping up, you need to do at least the core questions. The additional questions are more challenging and are designed to stretch the more confident programmers. Don't worry if you can't do them now, but be prepared to come back and try them later on.

Feedback: It is important that you get feedback on your exercise answers so that you know they are correct, that you are not making common mistakes, that the program code is properly presented and that you are confident you have solved the problem properly. To do this, get your answers reviewed by a lab demonstrator during lab sessions.

NOTE: You must keep all exercise answers as they form a record of your progress. After the exams you may be required to hand-in all exercises and coursework answers, as part of the course assessment process.


The Simple Test Class

These exercise questions are about testing. Below is an improved version of the test class presented in the lecture. Copy and modify this class for your exercise answers.

Class Person is the class being tested:

public class Person
{
  private String name;
  private int phoneNumber;
  public Person(String name, int phoneNumber)
  {
    this.name = name;
    this.phoneNumber = phoneNumber;
  }
  public String getName()
  {
    return name;
  }
  public int getNumber()
  {
    return phoneNumber;
  }
  public void setName(String name)
  {
    this.name = name;
  }
  public void setNumber(int phoneNumber)
  {
    this.phoneNumber = phoneNumber;
  }
} 

Class PersonTest is the test class, containing the infrastructure code and the test methods.

public class PersonTest
{
  private Person person; // Person object to be tested.
  private int count = 0; // Total number of test methods run.
  int fail = 0;          // Number of asserts failed.
  /**
   * setUp must be called before each test method, in order
   * to recreate the tests objects. The test objects should
   * be replaced before each test, so that the running of one
   * test does not affect the next.
   */
  public void setUp()
  {
    count++;
    person = new Person("P1", 123);
  }
  /**
   * Print a summary of the results after all the tests have
   * been run.
   */
  public void finish()
  {
    System.out.println("\nTest methods run: " + count);
    System.out.println("Tests failed: " + fail);
  }
  /**
   * An assert method to compare two string values. If the values
   * are the same, the assertion succeeds and a dot is printed to
   * show the success. Otherwise a fail message is displayed.
   * This method is called from the test methods.
   * Add additional versions of this method to compare values of
   * other types.
   *
   * @param s1     The expected value.
   * @param s2     The value being tested.
   * @param method The name of the test method this method was called from.
   */
  public void assertEquals(String s1, String s2, String method)
  {
    if (!s1.equals(s2))
    {
      fail++;
      System.out.print("\nTest failed in method: " + method + " - ");
      System.out.println(s1 + " not equal to " + s2);
    }
    else
    {
      System.out.print(".");
    }
  }
  /**
   * Assert method to compare two ints.
   *
   * @param n1     The expectec value.
   * @param n2     The value being tested.
   * @param method The name of the test method this method was called from.
   */
  public void assertEquals(int n1, int n2, String method)
  {
    if (!(n1 == n2))
    {
      fail++;
      System.out.print("\nTest failed in method: " + method + " - ");
      System.out.println(n1 + " not equal to " + n2);
    }
    else
    {
      System.out.print(".");
    }
  }
  /**
   * Test the Person class getName method. It should return the
   * name value passed to the constructor when a Person object
   * is created.
   */
  public void testGetName()
  {
    assertEquals(person.getName(), "P1", "testGetName");
  }
  /**
   * Test the Person class setName method. As setName is a void
   * method the getName method is used to check that setName worked
   * properly.
   */
  public void testSetName()
  {
    person.setName("NewName");
    assertEquals(person.getName(), "NewName", "testSetName");
  }
  /**
   * Test the Person class getNumber method. It should return the
   * number value passed to the constructor when a Person object
   * is created.
   */
  public void testGetNumber()
  {
    assertEquals(person.getNumber(), 123, "testNumber");
    // deliberate error, for example of what happens when a test fails
    assertEquals(person.getNumber(), 101, "testNumber");
  }
  /**
   * Test the Person class setNumber method. As setNumber is a void
   * method the getNumber method is used to check that setNumber worked
   * properly.
   */
  public void testSetNumber()
  {
    person.setNumber(987);
    assertEquals(person.getNumber(), 987, "testSetNumber");
  }
  /**
   * Run the tests. Note that the setUp method is called before
   * each test method and the finish method is called last.
   */
  public void runTests()
  {
    setUp();
    testGetName();
    setUp();
    testSetName();
    setUp();
    testGetNumber();
    setUp();
    testSetNumber();
    finish();
  }
  public static void main(String[] args)
  {
    PersonTest t = new PersonTest();
    t.runTests();
  }
}

To keep a record of test results, the output of a program can be saved to a file in two ways:

1. Using I/O redirection:

java Myprog >aFile

The `> aFile' part causes all output of the program to be written to the file aFile. However, the output cannot be seen on the screen.

2. Using the script command (on Unix) to record what is displayed in an xterm window. The command:

script aFile

will cause everything displayed in the terminal window, including what you type in, to be written to the file aFile. When you want to stop recording type Ctrl-D.


Core questions

Q7.1 Modify and extend the test class above to test this class:

import java.util.ArrayList;
public class Order
{
  private ArrayList orderItems;
  private int total;
  public Order()
  {
    orderItems = new ArrayList();
    total = 0;
  }
  public ArrayList getOrderItems()
  {
    return orderItems;
  }
  public void addOrderItem(String item, int price)
  {
    orderItems.add(item);
    total += price;
  }
  public int getTotal()
  {
    return total;
  }
}

Class Order represents a collection of items making up an order. The name of each item is recorded, along with the total cost of all the items.


Q7.2 Test the log method provided by the Java class Math.log. Do the following:

When comparing the actual results with the expected results you will need to consider the following:

Hint: Double numbers can be compared by subtracting one from the other and seeing if the result is less than a delta value. If it is then the difference between the numbers is small enough to consider the two numbers equal. For example:

Comparing 1.1235 and 1.1234, with a delta of 0.001:

1.1235 - 1.1234 = 0.0001

0.0001 < 0.001 = true, so the values are considered equal to 3 decimal places.


Q7.3 Repeat the strategy outlined in Q7.2 but this time write and test a square root method (sqrt). Do not use the existing Math.sqrt method but write your own instead. How accurate is your version of sqrt compared to the Math.sqrt version?

Hint: There are various ways of approximating square roots, for example, investigate the Newton-Raphson method.

Very Big Hint:

public static double sqrt(double x, double e)
{
  if (x < 0.0)
  {
    return Double.NaN ;
  }
  if (x <= e)
  {
    return 0.0 ;
  }
  double a = 1.0 ;
  while (Math.abs(a * a - x) > e)
  {
    a = (a + x / a) / 2 ;
  }
  return a ;
}

Q7.4 Write your own Date class to represent dates (don't make use of any of the Java library classes for working with dates!). Provide methods to add and subtract days, months and years from dates, and to find the number of days between two dates.
Test your date class using a modified version of the test program shown earlier. Your should make it clear for what range of dates your class works with. The range must include at least 1900-2100.


Q7.5 Write and test a Complex Number class. Include a suitable range of arithmetic operations.


Additional questions

Q7.6 Write and test a method that finds all permutations of the digits in an integer.


Q7.7 Write and test your own implementation of Quicksort.