Today we’ll explore how to use JUnit to test our code.

JUnit is an industrial-strength unit-testing framework. It’s well integrated in Eclipse, and easy to add to a project.

We can start by testing a class we’ve provided for you, Triangle. Create a new project in Eclipse and add this class to the cs284 package. Here’s the code for it:

package cs284;

public class Triangle {
  private int a, b, c;

  public Triangle(int a, int b, int c) {
    if (a + b < c) { throw new IllegalArgumentException("violated triangle inequality"); }

    if (a <= 0) { throw new IllegalArgumentException("a was " + a + ", must be positive"); }
    if (b <= 0) { throw new IllegalArgumentException("b was " + b + ", must be positive"); }
    if (c <= 0) { throw new IllegalArgumentException("c was " + c + ", must be positive"); }

    this.a = a;
    this.b = b;
    this.c = c;
  }

  public int perimeter() {
    return a + b + c;
  }

  public double area() {
    // Heron's formula
    double s = perimeter() / 2;

    return Math.sqrt(s * (s - a) * (s - b) * (s - c));
  }
}

Task #1

Your first task is to write some unit tests for Triangle.

  1. Click on “File > New > JUnit Test Case”. If you have Triangle.java open, it will suggest the default name TriangleTest and mark cs284.Triangle as the “class under test”. Perfect.
  2. You’ll be prompted to add JUnit 5 to the build path. Click “Ok”—this imports the library we need and creates a TriangleTest.java in the same cs284 package.

By default, our TriangleTest test case will be empty.

package cs284;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.Test;

class TriangleTest {

  @Test
  void test() {
    fail("Not yet implemented");
  }

}

The general format is to write a no-argument, void-returning method with the @Test annotation. JUnit will automatically find all such methods and run them.

A test passes when there are no uncaught exceptions; otherwise, it fails. JUnit’s Assertions class offers a bunch of static methods to make it easy to generate exceptions appropriately.

Note that the import is written import static ..., which imports the static methods so they’re callable just like functions. The most common ways to use them are:

As a first test case, let’s create a classic right triangle and ensure that its perimeter and area are correct. Here’s the recipe:

  1. Rename the test method to rightTriangle345.
  2. Remove the fail line.
  3. Create a Triangle with legs of length 3, 4, and 5.
  4. Use assertEquals to ensure that its perimeter is correct.
  5. Use assertEquals to ensure that its area is correct (5 must be the hypotenuse, so the base is 4 and the height is 3—or vice versa).

You can run your test by clicking the “run tests” button (not to scale):

a green circle with the 'play' triangle in it, next to a green/red bar

After running tests, you should be treated to a view of your test trace. If things went well, you have a green bar showing a single test passed.

Your code should also be highlighted in a variety of colors. These colors show your code coverage: how much your code was exercised by your tests? Green means the code has been unconditionally exercised. Yellow on a conditional means the conditional has been run, but the condition has only gone one way. Red means the code has never run.

Task #2

Now write more tests to get complete coverage. All of your code should turn green! (Imports and package declarations and bare curly braces won’t turn colors—just get it so that every “material” line of code is colored green.)

You might wonder… how can you test a failure, if failing a test means throwing an exception? Judicious use of try/catch helps you out. Suppose you have some function void explode(int) that throws a BigBoomException on positive inputs. To test this behavior, you might write:

try {
  explode(5); // should throw!
  fail("expected exception from explode(5)");
} catch (BigBoomException _e) { // underscore signals our intention to ignore the exception
  // we _appropriately_ got the exception---nice, it passes!
  // you could write more code to make sure that the exception is the "right" one
} catch (Throwable t) {
  fail("unexpected exception " + t);
}

Task #3

With the time remaining, try to write JUnit tests for your BinaryNumber implementation.