Part 6 – Assert for Exception in JUnit

What have we learned so far

Part 1 – What is Unit Testing?
https://www.onlyfullstack.com/what-is-unit-testing/

Part 2 – What is JUnit? How to use JUnit?
https://www.onlyfullstack.com/what-is-junit-how-to-setup-junit-in-eclipse/

Part 3 – Annotations used in JUnit
https://www.onlyfullstack.com/part-3-annotations-used-in-junit/

Part 4 – JUnit Assert Methods
https://www.onlyfullstack.com/junit-assert-methods/

Part 5 – Complete guide for Hamcrest JUnit
https://www.onlyfullstack.com/complete-guide-for-hamcrest-matchers/

How do you assert that a certain exception is thrown in JUnit 4 tests?

Let’s write some business logic which will throw an exception.
package com.onlyfullstack.unittesting.service;

import org.apache.commons.lang3.StringUtils;

/**
 * This class contains the business logic to throw an exception
 */
public final class ExceptionHandling {

    public String convertIntoUpperCase(String input) {
        if (StringUtils.isEmpty(input)) {
            throw new IllegalArgumentException("Empty value is passed.");
        }
        return input.toUpperCase();
    }
}

The convertIntoUpperCase() method will throw an IllegalArgumentException if an empty string is passed to the method.

There are 3 ways to assert a certain exception in Junit. Let’s write the unit test cases for it. 
1. try-catch idiom

This idiom is one of the most popular ones because it was used already in JUnit 3. This approach is a common pattern. The test will fail when no exception is thrown and the exception itself is verified in a catch clause.

@Test
public void convertIntoUpperCase_withInvalidInput_tryCatchIdiom() {
    try {
        exceptionHandling.convertIntoUpperCase("");
        fail("It should throw IllegalArgumentException");
    } catch (IllegalArgumentException e) {
        Assertions.assertThat(e)
                .isInstanceOf(IllegalArgumentException.class)
                .hasMessage("Empty value is passed.");
    }
}

2. @Test expected annotation

In this approach, we specify the expected exception in @Test as below
@Test(expected = IllegalArgumentException.class)

When the exception wasn’t thrown you will get the following message: java.lang.AssertionError: Expected exception: java.lang.IllegalArgumentException

With this approach, you need to be careful though. Sometimes it is tempting to expect general Exception, RuntimeException or even a Throwable. And this is considered as a bad practice because your code may throw an exception in other places than you actually expected and your test will still pass!

One of the drawback of this approach is you can’t assert for the exception message.

@Test(expected = IllegalArgumentException.class)
public void convertIntoUpperCase_withInvalidInput_testExpected() {
    exceptionHandling.convertIntoUpperCase("");
}

3. Junit @Rule
The same example can be created using ExceptedException rule. The rule must be a public field marked with @Rule annotation.

@Test
public void convertIntoUpperCase_withInvalidInput_ExpectedExceptionRule() {
    exception.expect(IllegalArgumentException.class);
    exception.expectMessage("Empty value is passed.");
    exceptionHandling.convertIntoUpperCase("");
}

I find the above code more readable hence I prefer to use this approach.

When the exception isn’t thrown you will get the following message: java.lang.AssertionError: Expected test to throw (an instance of java.lang.IllegalArgumentException and exception with the message “Empty value is passed.”). Pretty nice.

But not all exceptions I check with the above approach. Sometimes I need to check only the type of the exception thrown and then I use @Test annotation.

Source Code
Download the source code of JUnit tutorial from below git repository :
unit-testing-and-integration-testing-with-spring-boot