More about JUnit
Improving the output
Test cases should be short and focused on testing a single fact about the class under test. It is important to document each test case with a brief statement stating what is being tested. You can see an example of this in the inline comments inBasketballExample
.
When using JUnit, it is easy to make such comments part of the output produced when a test fails.
In each one of the assertEquals
methods we can optionally provide a string
message. If the test fails, this string will be printed along with the expected and actual values.
Here is an example.
@Test public void testInitialDiameter() { String msg = "A newly constructed basketball should have the diameter it was constructed with"; Basketball b = new Basketball(5); assertEquals(msg, 5.0, b.getDiameter(), EPSILON); }Now, when the test fails, the output from the failure trace includes the message explaining what was being tested.
A newly constructed basketball should have the diameter it was constructed with expected:<5.0> but was:<0.0>
Other assertion methods
The most useful methods are the various forms ofassertEquals
. (The third and fourth forms are
the ones used for comparing floating-point numbers.)
assertEquals(expectedValue, actualValue)
assertEquals(message, expectedValue, actualValue)
assertEquals(expectedValue, actualValue, epsilon)
assertEquals(message, expectedValue, actualValue, epsilon)
Sometimes you just want to assert that some boolean expression is true or not:
assertTrue(expression)
assertTrue(message, expression)
assertFalse(expression)
assertFalse(message, expression)
And sometimes you want to simply indicate that a test should simply fail if a certain point in the code is reached.
fail()
fail(message)
There are a few more that you will probably use only rarely, and you can find the details in the
JUnit API documentation. (Look for the class org.junit.Assert
.)
Before and After
You might notice in theBasketballTests
class that we had to create a
new Basketball
at the beginning of each test case. Almost all test cases
require some sort of initialization, usually the creation of one or more objects.
If the setup is the same for many of test cases,
you can use the @Before
annotation to designate a method that the framework
should run before each test case. For the BasketballTests
example, we could do
something like this. The setup
method will automatically run before
the execution of each test case to initialize the bb
variable. This can save
you from writing a lot of repetitive code.
package lab3; import org.junit.Test; import org.junit.Before; import static org.junit.Assert.assertEquals; public class BasketballTests2 { // margin of error for floating-point comparisons private static final double EPSILON = 10e-07; private Basketball bb; @Before public void setup() // runs before every test case { bb = new Basketball(5); } @Test public void testInitial() { assertEquals(false, bb.isDribbleable()); } @Test public void testInitialDiameter() { assertEquals(5.0, bb.getDiameter(), EPSILON); } @Test public void testInflate() { bb.inflate(); assertEquals(true, bb.isDribbleable()); } @Test public void testDiameterAfterInflation() { bb.inflate(); assertEquals(5.0, bb.getDiameter(), EPSILON); } }Note that you'll need to add an import statement for
org.junit.Before
.
Not surprisingly there is also an annotation @After
that designates a method to run
after each test case, but it is not nearly as useful as @Before
.