Software Testing: Tutorial 1 (Week 2, 19 January 2009)

Please try to attend the tutorials once they start. Attending tutorials is certainly a higher priority than attending lectures.

The purpose of this tutorial is to ensure that you are comfortable with the Eclipse/Java/JUnit tools on DICE (or whichever alternative tools that you choose to work with).

Source code

Recall the Triangle source code we looked at on Friday in Week 1 — first the badly-implemented Triangle class itself:

Source for st.Triangle:
package st;

public class Triangle {

  private int p; // Longest edge
  private int q;
  private int r;

  public Triangle(int s1, int s2, int s3) {
    // Ensure that p is the largest of the three.
    if(s1 > s2) {
      q = s2;
      if(s1 > s3) {
        p = s1; r = s3;
      } else {
        p = s3; r = s1;
      }
    } else {
      q = s1;
      if(s2 > s3) {
        p = s2; r = s3;
      } else {
        p = s3; r = s2;
      }
    }
  }

  public boolean isScalene() {
    return p > 0 && q > 0 && r > 0 && p < q + r &&
            (q < r || q > r);
  }

  public boolean isEquilateral() {
    return p == q && q == r;
  }

}

Then the TriangleTestJ3 unit test (this is JUnit 3 style; see below for a JUnit 4 version).

JUnit 3 source for st.TriangleTestJ3:
package st;

import junit.framework.TestCase;

public class TriangleTestJ3 extends TestCase {

  private Triangle t;

  public void setUp() {
    t = new Triangle(3, 4, 5);
  }

  public void testTriangle() {
    fail("Not yet implemented");
  }

  public void testIsScalene() {
    assertTrue("t not scalene!?!", t.isScalene());
  }

  public void testIsEquilateral() {
    assertFalse("t unexpectedly equilateral!", t.isEquilateral());
  }

}

Here's the JUnit 4 version:

JUnit 4 source for st.TrianglTestJ4:
package st;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class TrianglTestJ4 {

  private Triangle t;

  @Before
  public void init() throws Exception {
    t = new Triangle(3, 4, 5);
  }

  @Test public void scaleneOk() {
    assertTrue(t.isScalene());
  }

}

And finally the AllTestsJ3 test suite:

Source for st.AllTestsJ3:
package st;

import junit.framework.Test;
import junit.framework.TestSuite;

public class AllTestsJ3 {

  public static Test suite() {
    TestSuite suite = new TestSuite("Test for st");
    //$JUnit-BEGIN$
    suite.addTestSuite(TriangleTestJ3.class);
    //$JUnit-END$
    return suite;
  }

}

And the JUnit 4 version:

Source for st.AllTestsJ4:
package st;

import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
  st.TriangleTestJ4.class,
})
public class AllTestsJ4 {
  // Everything's done by the annotations above.
}

Things to do

Add tests

Think of a series of test inputs and the results that you'd expect for them, then create the appropriate tests. As I explained on Friday, the test fixture mechanism is clumsy for instantiating and testing a lot of objects, so you might prefer to get rid of the private Triangle t; and setUp(), and do something like this:

Testing lots of Triangles (JUnit 3 style):
public void testIsEquilateral() {
  Triangle t = new Triangle(4, 5, 6);
  assertFalse("(4, 5, 6) unexpectedly equilateral!", t.isEquilateral());
  t = new Triangle(1, 1, 1);
  assertTrue("(1, 1, 1) unexpectedly not equilateral!", t.isEquilateral());
  …
}

You might also like to add a toString() method to Triangle in order to help with debug messages.

Fix the implementation

These tests will quickly show that isScalene() is incorrect. Fix it.

Enhance the implementation

It's really an error to allow construction of bad triangles, so it makes sense to test these bad inputs in the constructor and throw an exception of some kind for non-triangle parameters. Do this, including adding a BadTriangleException to your implementation. You may need to add some throws clauses to your code. You should also find that after making these enhancements, you can simplify the isScalene() test.

Test the enhancements

As demonstrated in class, you can test that error conditions are detected and exceptions get thrown correctly too:

Testing to see that exceptions are thrown (JUnit3):
public void testTriangle() {
  try {
    // Some code which should cause an exception:
    Triangle q = new Triangle(0, 0, 0);
    // If we get here then the code didn't cause an exception,
    // and so the test has failed.
    fail("Exception not thrown: bad triangle accepted!!!");
  } catch(ParticularExceptionWeAreTestingFor e) {
    // If we get here then the exception was thrown correctly.
    // We do nothing, and let successful execution continue.
  }
  …
}

This is much cleaner in JUnit 4:

Testing to see that exceptions are thrown (JUnit4):
@Test(expected = st.BadTriangleException.class)
public void noBadTriangles() throws BadTriangleException {
  // Should cause an exception:
  t = new Triangle(0, 0, 0);
}

Implement a set of tests for the testTriangle method.


Version 1.5, 2010/01/21 19:01:21


Home : Teaching : Courses : St : 2008-9 

Informatics Forum, 10 Crichton Street, Edinburgh, EH8 9AB, Scotland, UK
Tel: +44 131 651 5661, Fax: +44 131 651 1426, E-mail: school-office@inf.ed.ac.uk
Please contact our webadmin with any comments or corrections. Logging and Cookies
Unless explicitly stated otherwise, all material is copyright © The University of Edinburgh