Week 3: JUnit
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
.
- Click on “File > New > JUnit Test Case”. If you have
Triangle.java
open, it will suggest the default nameTriangleTest
and markcs284.Triangle
as the “class under test”. Perfect. - 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 samecs284
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:
assertEquals(expected, got)
will throw an exception if!expected.equals(got)
assertEquals(expected, got, message)
behaves the same, but will includemessage
fail(message)
just fails
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:
- Rename the
test
method torightTriangle345
. - Remove the
fail
line. - Create a
Triangle
with legs of length 3, 4, and 5. - Use
assertEquals
to ensure that its perimeter is correct. - 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):
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.