Labs ICT
โญ Pro Login

Testing Exceptions

3 min read | JUnit Testing
โญ

Want the full learning experience?

Get structured courses, certificates, projects, and instructor support with LabsICT Pro.

Explore Pro Courses

One of the most common things you need to test is whether your code throws the right exceptions under the right conditions. JUnit 5 makes this straightforward with assertThrows. Let us see how to test exceptions properly.

Basic Exception Testing


import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class ExceptionTest {

    @Test
    void shouldThrowException() {
        assertThrows(IllegalArgumentException.class, () -> {
            throw new IllegalArgumentException("Invalid input");
        });
    }

    @Test
    void calculatorShouldThrowOnDivideByZero() {
        Calculator calc = new Calculator();
        assertThrows(ArithmeticException.class,
            () -> calc.divide(10, 0)
        );
    }
}
    

Checking the Exception Message

Often you want to verify not just the exception type, but also the message:


@Test
void shouldThrowWithSpecificMessage() {
    IllegalArgumentException exception = assertThrows(
        IllegalArgumentException.class,
        () -> validateAge(-5)
    );
    assertEquals("Age must be positive", exception.getMessage());
}

@Test
void shouldThrowWithMessageContaining() {
    IllegalArgumentException exception = assertThrows(
        IllegalArgumentException.class,
        () -> validateEmail("not-an-email")
    );
    assertTrue(exception.getMessage().contains("invalid email"));
}
    

Testing Multiple Exceptions


@Test
void shouldThrowDifferentExceptions() {
    // Test case 1: null input
    assertThrows(NullPointerException.class,
        () -> processString(null)
    );

    // Test case 2: empty input
    assertThrows(IllegalArgumentException.class,
        () -> processString("")
    );

    // Test case 3: valid input (no exception)
    assertDoesNotThrow(() -> processString("hello"));
}

@Test
void shouldNotThrowException() {
    // assertDoesNotThrow verifies NO exception is thrown
    assertDoesNotThrow(() -> {
        int result = 2 + 2;
        assertEquals(4, result);
    });
}
    

Exception Assertion with Executable

You can capture the exception and do more complex assertions:


@Test
void shouldThrowWithCause() {
    Exception exception = assertThrows(RuntimeException.class, () -> {
        try {
            throw new IOException("File not found");
        } catch (IOException e) {
            throw new RuntimeException("Failed to process", e);
        }
    });

    assertEquals("Failed to process", exception.getMessage());
    assertTrue(exception.getCause() instanceof IOException);
    assertEquals("File not found", exception.getCause().getMessage());
}
    

JUnit 4 vs JUnit 5 Exception Testing


// JUnit 4 approach
@Test(expected = ArithmeticException.class)
public void divideByZero() {
    calc.divide(10, 0);
}

// JUnit 5 approach (more powerful)
@Test
void divideByZero() {
    ArithmeticException exception = assertThrows(
        ArithmeticException.class,
        () -> calc.divide(10, 0)
    );
    assertEquals("Cannot divide by zero", exception.getMessage());
}
    

The JUnit 5 approach is better because you can inspect the exception object, check the message, verify the cause, and do other assertions. JUnit 4 only let you check the exception type.

Common Patterns


class InputValidatorTest {

    @Test
    void rejectsNull() {
        assertThrows(NullPointerException.class,
            () -> validator.validate(null));
    }

    @Test
    void rejectsEmptyString() {
        IllegalArgumentException ex = assertThrows(
            IllegalArgumentException.class,
            () -> validator.validate("")
        );
        assertEquals("Input cannot be empty", ex.getMessage());
    }

    @Test
    void rejectsTooLong() {
        String longString = "a".repeat(1001);
        assertThrows(IllegalArgumentException.class,
            () -> validator.validate(longString));
    }

    @Test
    void acceptsValidInput() {
        assertDoesNotThrow(() -> validator.validate("hello"));
    }
}
    

AssertThrows Returns the Exception

A key detail: assertThrows returns the exception that was thrown. This lets you chain assertions:


@Test
void detailedExceptionCheck() {
    ValidationException ex = assertThrows(
        ValidationException.class,
        () -> service.process(new Request("", -1))
    );

    assertEquals(2, ex.getErrors().size());
    assertEquals("name", ex.getErrors().get(0).getField());
    assertEquals("amount", ex.getErrors().get(1).getField());
}
    

๐Ÿงช Quick Quiz

Which class is used to test exceptions in JUnit 5?