Appearance
Exception Testing
Exception handling is very important in Java programs.
The methods we write ourselves often throw various exceptions. Testing for possible exceptions is an essential part of testing.
Therefore, when writing JUnit tests, in addition to normal input and output, we also need to specifically test scenarios that may lead to exceptions.
Let's continue using the Factorial
example:
java
public class Factorial {
public static long fact(long n) {
if (n < 0) {
throw new IllegalArgumentException();
}
long r = 1;
for (long i = 1; i <= n; i++) {
r = r * i;
}
return r;
}
}
At the beginning of the method, we added a check for the parameter n
. If it is negative, it directly throws an IllegalArgumentException
.
Now, we want to test for exceptions. In JUnit tests, we can write a @Test
method specifically to test exceptions:
java
@Test
void testNegative() {
assertThrows(IllegalArgumentException.class, new Executable() {
@Override
public void execute() throws Throwable {
Factorial.fact(-1);
}
});
}
JUnit provides assertThrows()
to expect the capture of a specified exception. The second parameter, Executable
, encapsulates the code that is expected to throw an exception. When we execute Factorial.fact(-1)
, it must throw an IllegalArgumentException
. If assertThrows()
captures the specified exception, the test passes. If no exception is thrown or a different type of exception is thrown, the test fails.
Some developers may find writing an anonymous Executable
class too cumbersome. In fact, starting with Java 8, functional programming was introduced, and all single-method interfaces can be simplified as follows:
java
@Test
void testNegative() {
assertThrows(IllegalArgumentException.class, () -> {
Factorial.fact(-1);
});
}
The unusual ->
syntax above is the implementation of a functional interface. We will cover this in more detail later. For now, we can simply use this fixed code structure to write statements that throw exceptions.
Exercise
Observe the Factorial.fact()
method. Note that because the long
integer has a range limit, when we pass in the parameter 21
, the result is -4249290049419214848
instead of the expected 51090942171709440000
. Therefore, when the parameter is greater than 20
, the Factorial.fact()
method should throw an ArithmeticException
. Please write tests and modify the implementation code to ensure the tests pass.
Summary
- Testing exceptions can be done using
assertThrows()
, expecting to capture a specified type of exception. - Every possible type of exception that might occur must be tested.