Testing Your Class
What to Test
In the world of object-oriented programming, problems are solved and systems are designed using a collection of interacting classes. But before we can try to get classes to interact, we need to make sure each class functions properly according to its specification. We need to verify that all public methods behave as advertised under each set of circumstances -- they perform their assigned function and return the appropriate value, object, or array.
How to Test
Java allows every class to implement its own main. We can use main to write test code for the class. The primary focus of this testing is the public methods of the class. In main we perform the following basic steps. Note that we can use hard coded values when we call constructors, mutators, and other class methods. There's no need for user input.
  1. Instantiate an object of our class type using one of its constructors. We can use this object to call both static and non-static methods of our class
  2. Call all accessors for the object created above and verify their return values. If some instance variables have no accessors, use the toString method to output the values of these instance variables.
  3. Use the object to call each public method with hard-coded arguments. Use many calls with a variety of arguments so that every logic path in the method is used. First, call the method using "normal" arguments which cause the method to execute the "normal" path with no error conditions. Then call each method with arguments that cause the method to "fail" in every possible way.
  4. Repeat the above for each constructor.
A Testing Example
The class definition below represents a Ship used in the game of BattleShip. The ship's constructor tells the ship which squares it occupoes in a 2-dimensional "ocean". The attack method accepts the row/column coordinates of a square and determines if the ship occupies that square in the ocean. If not, attack returns MISS. If so, the ship has been hit and attack determines whether all squares that the ship occupies have been hit. If so, attack returns SUNK, else it returns HIT. The application that uses the Ship class guarantees that attack is never called with a particular row/column more than once. If this were not the case, we would make other calls to attack for this case as well.

public class Ship {
	public static final int VERTICAL = 0;
	public static final int HORIZONTAL = 1;
	public static final int HIT = 0;
	public static final int MISS = 1;
	public static final int SUNK = 2;
	
	private int size, orientation;
	private String name;
	private int nrHits;
	private int startRow, endRow, startColumn, endColumn;
	
	/**
	 * Preconditions: ????
	 * Postcondition: A well-formed ship object is created
	 * 
	 * @param name - The name of the Ship
	 * @param orientation - One of the constants VERTICAL or HORIZONAL
	 * @param size - The number of adjacent squares the ship occupies on the grid
	 * @param row 
	 * 	If orientation is HORIZONTAL, this is the row the ship is on
	 * 	If orientation is VERTICAL, this is the row of the upper-most square the ship occupies
	 * @param column
	 *  If orientation is HORIZONTAL, this is the column of the left-most square the ship occupies
	 *  If orientation is VERTICAL, this is the column the ship is in
	 */
	public Ship (String name, int orientation, int size, int row, int column)
	{
		// code here
	}
	
	/**
	 * Determines whether an attack on the ship was a MISS, a HIT, or a hit
	 * which SUNK the ship
	 * Preconditions:
	 * Postconditions: :	if a hit, the number of hits is incremented.
	 *	If the number of hits == size, then we've been sunk
	 * 
	 * @param row The row coordinate of the attack
	 * @param col The column coordinate of the attack
	 * @return One of Ship constants HIT, MISS, or SUNK
	 */
	public int attack( int row, int col)
	{
		// code here
	}
	
    public String getName( )
    {
    	return name;
    }
    
    public int getNrHits( )
    {
    	return nrHits;
    }
	

In main, we instantiate a ship object and call all accessors to validate their values after construction. We then first call attack with parameters which are known to miss the ship. We print attack's return value to verify that MISS was actually returned. We next call attack with parameters which we know will be a hit, and again print the return value to verify that attack actually returned HIT. We call attack with paramters that continue to hit the ship until all but one square has been hit. Finally we call attack for the last square and verify that attack returns SUNK.


	public static void main (String args[])
	{
		// first create a Ship object
		Ship s = new Ship("BattleShip", Ship.HORIZONTAL, 5, 2, 2);
        
 		// call all accessors and check the value returned
        if (! s.getName( ).equals("BattleShip"))
        	System.out.println( "getName failed");

		if (s.getNrHits( ) != 0)
        	System.out.println( "nrHits not zero after construction");

        // attack the ship, but MISS on purpose.
        // validate return value from attack and nrHits not incremented
		int attackResult;
        attackResult = s.attack(1,1);
		if (attackResult != MISS)
          	System.out.println( "attack at 1,1 was not a MISS");
        if (s.getNrHits( ) != 0)
        	System.out.println( "nrHits not zero after initial MISS");

        // attack the ship, and HIT on purpose
        attackResult = s.attack(1,1);
		if (attackResult != MISS)
          	System.out.println( "attack at 1,1 was not a MISS");
        if (s.getNrHits( ) != 0)
        	System.out.println( "nrHits not zero after initial MISS");

        attackResult = s.attack(2, 2);
		if (attackResult != HIT)
          	System.out.println( "attack at 2,2 was not a HIT");
        if (s.getNrHits( ) != 1)
        	System.out.println( "nrHits not 1 after initial HIT");
        
        // hit all but one of the remaining squares the ship occupies
        s.attack( 2, 3 );
        s.attack( 2, 4 );        
        s.attack( 2, 5 );

		// now attack the last square to SINK the ship
        attackResult = s.attack(2, 6);
		if (attackResult != SUNK)
          	System.out.println( "attack at 2,5 was not SUNK");
        if (s.getNrHits( ) != 5)
        	System.out.println( "nrHits not 5 sinking ship");
	}