Skip to content
On this page

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.
Exception Testing has loaded