Test for Exception in JUnit 5 and JUnit 4

In this tutorial, you will learn how to write a JUnit test that validates whether the method under test throws the correct exception. The tutorial includes examples for both JUnit 5 and JUnit 4.

For video lessons, check out my video course: Testing Java with JUnit and Mockito.

Let’s begin with JUnit 5 version first.

JUnit 5. “assertThrows()” Assertion

To test for exceptions in JUnit 5 we use the assertThrows() assertion. To review other available JUnit 5 assertions have a look at the JUnit 5 Assertions with Examples tutorial.

Let’s assume that we need to validate if our method under test throws ArithmeticException.  To assert that our method under test throws ArithmeticException, we can use the assertThrows() assertion the following way:

@Test
@DisplayName("Division by zero")
void testIntegerDivision_WhenDividendIsDividedByZero_ShouldThrowArithmeticException() {
   
    // Arrange
    int dividend = 4;
    int divisor = 0;
    String expectedExceptionMessage = "/ by zero";

    // Act & Assert
    ArithmeticException actualException = assertThrows(ArithmeticException.class, ()->{
       // Act
        calculator.integerDivision(dividend, divisor);
    }, "Division by zero should have thrown an Arithmetic exception.");

    // Assert
    assertEquals(expectedExceptionMessage, actualException.getMessage(),
            "Unexpected exception message");

}

Can assertThrows() be used to check for multiple exceptions?

Yes, assertThrows() can be used to check for multiple exceptions in the same test case.

To check for multiple exceptions, you can use the assertThrows() method multiple times in the same test case, each time checking for a different type of exception. Alternatively, you can use the overloaded version of assertThrows() that accepts multiple types of exceptions as arguments, like this:

assertThrows(ThrowableType1.class, executable);
assertThrows(ThrowableType2.class, executable);
// ... check for other exceptions

where ThrowableType is the type of the expected exception and executable is a lambda expression or a method reference that represents the code block being tested.

Can assertThrows() be used with asynchronous code in JUnit 5?

Yes, JUnit 5 provides support for testing asynchronous code using the CompletableFuture class and the @Timeout and @Test annotations. The assertThrows() method can also be used with asynchronous code to check if the expected exception is thrown.

Here’s an example of how you can use assertThrows() with asynchronous code in JUnit 5:

@Test
@Timeout(5) // set a timeout of 5 seconds for the test
void testAsyncCode() {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        // asynchronous code that throws an exception
        throw new RuntimeException("error message");
    });

    // use assertThrows() to check if the expected exception is thrown
    Throwable exception = assertThrows(CompletionException.class, future::join);

    // use assertions to check the exception message or other details
    assertEquals("error message", exception.getCause().getMessage());
}

In this example, the CompletableFuture.supplyAsync() method is used to run the asynchronous code that throws an exception. The assertThrows() method is used to check if a CompletionException is thrown when calling future.join(), which waits for the asynchronous operation to complete and retrieves its result or throws an exception. Finally, assertions are used to check the exception message or other details.

It’s important to note that when testing asynchronous code, it’s important to use a timeout to prevent the test from hanging indefinitely if the asynchronous operation does not complete. The @Timeout annotation is used to set a timeout for the entire test, and you can also set a timeout for individual assertions using the assertTimeout() method.

assertThrows() vs assertDoesNotThrow()

assertThrows() and assertDoesNotThrow() are two methods in unit testing frameworks that are used to check if a code block throws an exception or not. The main difference between them is as follows:

assertThrows(): This method checks if the code block being tested throws a specific type of exception. If the exception is not thrown, the test case fails. This method is used to ensure that a certain code block behaves as expected and throws an exception under certain conditions.

assertDoesNotThrow(): This method checks if the code block being tested does not throw any exception. If an exception is thrown, the test case fails. This method is used to ensure that a certain code block behaves as expected and does not throw any unexpected exceptions.

JUnit 5 and 4. Using the Try and Catch.

When using the Try and Catch approach, you will need to catch the exception you are expecting first and then, inside of the catch{} block assert that the exception object is of the type you are expecting or its getMessage() method contains the message you are expecting.

Also, you will need to add the call to fail() inside of the try{} block. This is needed to make sure the test fails if the code does not throw an Exception your expect.

@Test
public void testWithTryAndCatchNullPointerException() {
    String name = getName();
    try {
        System.out.println(name.length());
        fail();
    } catch (NullPointerException ex) {
        assertTrue(ex instanceof NullPointerException);
    }
}

JUnit 4. Using @Test Expected Attribute

One way to test for expected exceptions is to use the @Test’s “expected” attribute. The “expected” attribute is used to specify that the method below should throw an exception specified by the attribute. For example:

@Test( expected = NullPointerException.class )

I have created a very simple method that if run should throw a NullPointerException and the line above with the @Test( expected = ) attribute will make the test case pass.

package myunittests;

import org.junit.Test;

 
public class ExpectingExceptionTest {
    
    @Test(expected = NullPointerException.class)
    public void testNullPointerException()
    {
        String name = getName();
        System.out.println(name.length());
    }
    
    private String getName()
    {
        return null;
    }
}

JUnit 4. Using the @Rule ExpectedException

With the @Rule annotation, we will first need to declare the ExpectedException and annotate it with @Rule. Inside of the Test method, we will then simply need to specify the Exception we are expecting this method to throw using the expect() method of the ExpectedException object.

package myunittests;

import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

 
public class ExpectingExceptionTest {
    
    @Rule
    public ExpectedException thrown = ExpectedException.none();

    
    @Test 
    public void testUsingTheRuleNullPointerException()
    {
        thrown.expect(NullPointerException.class);
        String name = getName();
        System.out.println(name.length());
    }

    private String getName() {
        return null;
    }
}

Hope that this short blog post was helpful to you. If you are interested to learn more about how to test Java with JUnit and Mockito, please have a look at my video course below.

Also, head over to the Testing Java Code page, where you’ll uncover a wealth of tutorials focused on the proper structure and optimization of test cases, allowing you to elevate the quality of your code.

Video Lessons

For video lessons, check out my video course: Testing Java with JUnit and Mockito.

Leave a Reply

Your email address will not be published. Required fields are marked *