Software Testing: Getting started with Eclipse and JUnit (JUnit 4)

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

A brief note on JUnit 3 and JUnit 4

There are two versions of JUnit around, 3 and 4. JUnit 4 is more modern, and so generally to be preferred. However, JUnit 4 requires language features introduced in Java 1.5, so the more backward-compatible JUnit 3 is still in widespread use, and it may be interesting for you to know the basics of its operation. Also, of particular note to those of you who prefer not to use the Eclipse environment, the version of Ant installed on DICE, 1.6.5, is incompatible with JUnit 4. Consequently if you want to use Ant to run your builds from the command line you'll need to write any tests you create for this course using JUnit 3 or come up with some other kind of personal workaround.

Unfortunately one of the tools we use, Coverlipse, is also incompatible with JUnit 4, so'll probably need to know how to write JUnit 3 tests for part of one of the assignments.

So even though I'd like to recommend that you always use JUnit 4 when writing tests, I can't --- you may find it helpful to work through the JUnit 3 version of this activity as well.

Source code

Recall the Triangle source code we looked at in class — 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 TriangleTestJ4 unit test:

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 AllTestsJ4 test suite:

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.
}

Activities

The activities here are partly to remind you of some relevant bits of Java and JUnit that should be useful later in the course, so please do read any online tutorials on Java/JUnit that you think will help.

  1. Add tests: Think of a series of test inputs for isScalene(), and the results that you'd expect for them, then create the appropriate tests (if you're at a loss, use the answer sheet from Lecture 1's quiz). As I explained in class, 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:
    @Test public void testIsEquilateral() {
      Triangle t = new Triangle(4, 5, 6);
      // (4, 5, 6) isn't equilateral, so expect a false return.
      assertFalse("(4, 5, 6) unexpectedly equilateral!", t.isEquilateral());
      t = new Triangle(1, 1, 1);
      // (1, 1, 1) is equilateral, so expect a true return.
      assertTrue("(1, 1, 1) unexpectedly not equilateral!", t.isEquilateral());
      …
    }
    

    Notice that the strings above (“(4, 5, 6) unexpectedly equilateral!” etc.) are messages that are printed if the test fails, so you put them in to help yourself understand what's happening if tests start to fail. The messages only need to say as much as you think would be useful.

  2. Add a Triangle.toString() method: If you add a toString method to Triangle, that'll help with debug messages (this tutorial on Java's toString() method might be of use).
  3. Fix the implementation: A thorough set of tests will quickly show that isScalene() is incorrect. Fix it.
  4. 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 may also find that after making these enhancements, you can simplify the isScalene() implementation and remove some of your test cases.
  5. 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:
    @Test(expected = st.BadTriangleException.class)
    public void noBadTriangles() throws BadTriangleException {
      // Should cause an exception:
      t = new Triangle(0, 0, 0);
    }
    

    … the expected = st.BadTriangleException.class above tells JUnit 4 to expect this method to throw an exception.

    Implement a set of tests for the Triangle constructor.

Solutions

Follow this link for some ideas for solutions to this activity.


Version 1.4, 2011/01/14 01:00:24


Home : Teaching : Courses : St : 2010-2011 

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