A slightly different way to do Boolean assertions in JUnit
In my opinion, the most useful assertion in JUnit is assertEquals()
. Once in a while, though, assertTrue()
comes in handy. Its single parameter form takes a Boolean, its 2-parameter form takes an assertion message and a Boolean (in JUnit 5, the Boolean goes first, before the assertion message).
I’m going to use a toy example here, since I just want to focus on the syntax. Create a Refrigerator
class somewhere in Source Packages (don’t write any explicit constructors), and a corresponding RefrigeratorTest
class in Test Packages. In the test class, put in the following:
@Test
public void testFridgeTempProperRange() {
Refrigerator fridge = new Refrigerator();
double temperature = fridge.getTemperature();
assertTrue("Temp too cold", temperature > 10.0);
assertTrue("Temp too high", temperature < 40.0);
}
Degrees are in Fahrenheit, but we’re not too concerned about that detail for this toy example.
Write getTemperature()
so it gives a temperature outside the proper range. Run the test. It should fail. If the temperature is too cold, your console might show something like this:
Testcase: testFridgeTempProperRange(basicexercises.RefrigeratorTest): FAILED
Temp too cold
junit.framework.AssertionFailedError: Temp too cold
at basicexercises.RefrigeratorTest.testGetTemperature(RefrigeratorTest.java:48)
You can get the same result a slightly different way, using Java’s reserved keyword assert
, which was added in Java 1.4.
The basic assert
syntax is
assert condition;
where condition
is Boolean (it may be a primitive variable, a wrapped primitive, or perhaps more commonly, a Boolean expression).
If condition
is false, an AssertionError
is thrown, but only if assertions are enabled, and most of the time they’re not. With assertions disabled, the assert
statement does nothing at all.
However… during the course of running JUnit tests, assertions are enabled. Or they should be; if they’re not, you might have to tinker with your build tool’s settings. Let’s rewrite our test thus:
@Test
public void testFridgeTempProperRange() {
Refrigerator fridge = new Refrigerator();
double temperature = fridge.getTemperature();
assert temperature > 10.0;
assert temperature < 40.0;
}
Your IDE should display “assert
” with the same font and color as “double
.” For example, in NetBeans, “assert
” is blue while “assertTrue
” is black and in italics.
Without changing getTemperature()
, run the test again.
Testcase: testFridgeTempProperRange(basicexercises.RefrigeratorTest): FAILED
null
junit.framework.AssertionFailedError
at basicexercises.RefrigeratorTest.testFridgeTempProperRange(RefrigeratorTest.java:47)
From this you should be able to figure out that what happened here went something like this: the JUnit test runner caught the AssertionError
and wrapped it into an AssertionFailedError
; the former comes from java.lang
, the latter from JUnit (and it extends the former).
However, instead of the message “Temp too cold” or “Temp too hot,” we just got “null” for a message. That’s fixed easily enough with an optional assert
syntax to specify a message for the AssertionError
.
@Test
public void testFridgeTempProperRange() {
Refrigerator fridge = new Refrigerator();
double temperature = fridge.getTemperature();
assert temperature > 10.0 : "Temp too cold";
assert temperature < 40.0 : "Temp too hot";
}
Then, in the console, after running the test again:
Testcase: testFridgeTempProperRange(basicexercises.RefrigeratorTest): FAILED
Temp too cold
junit.framework.AssertionFailedError: Temp too cold
at basicexercises.RefrigeratorTest.testFridgeTempProperRange(RefrigeratorTest.java:47)
If assertions are enabled for the test class, they should also be enabled in the class under test, but most likely only in the testing context. Add the following lines to Refrigerator
:
public void recalibrateThermometer() {
// PLACEHOLDER FOR RECALIBRATION TASKS
double temperature = this.getTemperature();
assert temperature > 10.0 : "Recalibration problem";
assert temperature < 40.0 : "Recalibration problem";
}
public static void main(String[] args) {
Refrigerator fridge = new Refrigerator();
System.out.println("Fridge temperature is "
+ fridge.getTemperature()
+ " degrees.");
System.out.println("Recalibrating, don't open door...");
fridge.recalibrateThermometer();
System.out.println("Fridge temperature is "
+ fridge.getTemperature()
+ " degrees");
System.out.println("Recalibration successful.");
}
Run Refrigerator.main()
and you might get output like this:
run:
Fridge temperature is 42.7 degrees.
Recalibrating thermometer, don't open door...
Fridge temperature is 42.7 degrees.
Recalibration successful.
BUILD SUCCESSFUL (total time: 0 seconds)
Either of the two assertions in recalibrateThermometer()
should have caused the program to stop before it made the false claim that the recalibration was successful. But with assertions not enabled, that did not happen.
Now add this test to RefrigeratorTest
:
@Test
public void testRecalibrateThermometer() {
System.out.println("recalibrateThermometer");
Refrigerator fridge = new Refrigerator();
fridge.recalibrateThermometer();
}
Even though this test doesn’t include any assertions, it should still fail if getTemperature()
is still giving a temperature outside the proper range.
Testcase: testRecalibrateThermometer(basicexercises.RefrigeratorTest): FAILED
Recalibration problem
junit.framework.AssertionFailedError: Recalibration problem
at basicexercises.Refrigerator.recalibrateThermometer(Refrigerator.java:22)
at basicexercises.RefrigeratorTest.testRecalibrateThermometer(RefrigeratorTest.java:56)Testcase: testFridgeTempProperRange(basicexercises.RefrigeratorTest): FAILED
Temp too hot
junit.framework.AssertionFailedError: Temp too hot
at basicexercises.RefrigeratorTest.testFridgeTempProperRange(RefrigeratorTest.java:48)
Well, there you have it, a slightly different way to do a Boolean assertion in JUnit. This other way is not necessarily better. You might like it better than assertTrue()
. You might not.
Maybe you consider assert
to be cleaner, as the parentheses are not required (though you can put them in if you want, and then you don’t even need a space after “assert
”). By the way, Scala also has assert, but I haven’t really looked into it yet.
Depending on the situation, JUnit might have a better way to write a test than with either assert
or assertTrue()
:
- Rather than
assert expected == actual
, useassertEquals(expected, actual)
- Rather than
assert Math.abs(expected - actual) < delta
, useassertEquals(expected, actual, delta)
- Rather than
assert expected[i] == actual[i]
within a For loop, useassertArrayEquals(expected, actual)
(and then you don’t have to write an assertion for the array lengths) - Rather than
assert unexpected != actual
, useassertNotEquals(unexpected, actual)
- Rather than
assert obj == null
orassert obj != null
, useassertNull(obj)
orassertNotNull(obj)
- Rather than
assert false
, usefail()
As for assert !condition
, it might be clearer to use assertFalse(condition)
.
With assert
and both assertTrue()
and assertFalse()
it’s important to include an assertion message, because those don’t get reported in failure as helpfully as a failing assertEquals()
.