Explain Codes LogoExplain Codes Logo

How to assert that a certain exception is thrown in JUnit tests?

java
prompt-engineering
interview-preparation
assertions
Nikita BarsukovbyNikita Barsukov·Sep 26, 2024
TLDR

Exception testing is quick and concise using **assertThrows**:

assertThrows(ExpectedException.class, () -> { // Fall into the pit of exceptions });

To easily check if an exception occurs, import the following for JUnit 5:

import static org.junit.jupiter.api.Assertions.assertThrows;

This effectively checks if your code is courteous enough to throw the expected exception.

Let's sink our teeth deeper into the topic, shall we?

JUnit 4: Oldie but Goodie

[Test @Test annotation & ExpectedException Rule]

Older versions of JUnit, particularly JUnit 4, offer a couple of gold nuggets for exception testing:

  • @Test(expected=Exception.class): Great for simple scenarios. Just testing for the exception type? Say less.
@Test(expected = NullPointerException.class) public void WhoForgotToInitialize() { // Oops! Someone's in trouble ... }
  • ExpectedException Rule: For nitpickers who need more granularity. Asserts exception type and message.
@Rule public ExpectedException thrown = ExpectedException.none(); @Test public void WellDidYouSeeThatComing() { thrown.expect(IndexOutOfBoundsException.class); thrown.expectMessage("Index: 0, Size: 0"); // We might run out of bounds...or do we? ... }

Asserting with try-catch

[Use try-catch for custom behaviors]

Sometimes, testing scenarios don't fit into standard frameworks. For those extra kick, try some good old try-catch:

@Test public void NopeNotToday_Exception() { try { // Irritate the code enough to throw an exception ... fail("Expected an IndexOutOfBoundsException to be thrown"); } catch (IndexOutOfBoundsException anEx) { assertThat(anEx.getMessage(), containsString("Size: 0")); // Better luck next time! } }

JUnit 5: Shiny and New

[assertThrows for JUnit 5]

JUnit 5 brings more goodies to the table. The assertThrows function not only checks the exception but also allows for a closer examination:

@Test void SecretsRevealed_ExceptionThrown() { SomeException thrown = assertThrows( SomeException.class, () -> { ... }, "Expected doThing() to throw, but it didn't" // Looks like doThing is not playing along! ); assertTrue(thrown.getMessage().contains("Specific detail")); }

Not Just Built-in Tools

[Assertion libraries: AssertJ & Google Truth]

Don't wear your JUnit blinders. Expand your toolbox with third-party libraries like AssertJ and Google Truth:

import static org.assertj.core.api.Assertions.assertThatThrownBy; @Test void AhaGotcha_Exception() { assertThatThrownBy(() -> { ... }) .isInstanceOf(IndexOutOfBoundsException.class) .hasMessageContaining("Index: 0, Size: 0"); //Caught you, red-handed! }

Be mindful about these while writing tests:

  • Doing Less: Simple is better. Keep your tests clean and easy to read.
  • Avoiding Overkill: Check the exception type and message. Forget stack traces unless you really, really need it.
  • One Behavior to Test Them All: Don't test multiple exceptions in one method, unless you're fond of headaches.

Advanced maneuvers

We're not finished yet. Brace yourself!

Parameterized tests

[Use @ParameterizedTest for multiple inputs and exceptions]

That's right, multiple inputs and exceptions per test. Thank JUnit 5 and parameterized tests:

@ParameterizedTest @ValueSource(strings = {"", " "}) void AreYouTalking_ExceptionCaught(String input) { assertThrows(IllegalArgumentException.class, () -> { ... }); // Not on my watch, empty strings! }

Chain of causation

[Test cause of exception]

If your exception has a cause, you might want to verify it too. Take a closer look with assertThrows:

@Test void TooExceptionsDeep_CauseAndEffect() { Throwable thrown = assertThrows(WrapperException.class, () -> { ... }); assertThat(thrown.getCause()).isInstanceOf(InternalException.class); // We've gotta go deeper! }

Custom alert

[Add custom error messages]

Want to brighten up test failures? Throw in some custom error messages:

@Test void PlainlyMisunderstood_ExceptionMessage() { Exception exception = assertThrows(SomeException.class, () -> { ... }); assertEquals("Expected error message", exception.getMessage()); // Now that's a message! }