Using try-catch blocks combined with fail() to test for the presence or absence of exceptions is an anti-pattern.

Why is this an issue?

Modern assertion libraries have made the clunky try-fail-catch pattern obsolete by introducing cleaner alternatives. For example, starting with JUnit 5, JUnit Jupiter provides assertThrows and assertDoesNotThrow. AssertJ offers similar methods.

Using try-catch with fail() for the same purpose adds boilerplate, makes the test intent less explicit, and is harder to maintain. Dedicated exception assertions also make it straightforward to keep asserting on the exception when one is expected.

How to fix it in JUnit

Replace try-catch blocks that rely on fail() to verify exception behavior with dedicated exception assertions from your testing library:

Code examples

Noncompliant code example

@Test
void testNoExceptionThrown() {
  try {
    userService.registerUser(validUser);
  } catch (ValidationException e) {
    fail("Should not have thrown any exception");
  }
}

Compliant solution

@Test
void testNoExceptionWithAssertion() {
  assertDoesNotThrow(() -> userService.registerUser(validUser));
}

Noncompliant code example

@Test
void testExceptionIsThrown() {
  try {
    userService.registerUser(invalidUser);
    fail("Expected ValidationException to be thrown");
  } catch (ValidationException e) {
    // Test passes, but code is verbose
    assertEquals("Invalid email", e.getMessage());
  }
}

Compliant solution

@Test
void testExceptionWithAssertion() {
  ValidationException exception = assertThrows(ValidationException.class, () -> userService.registerUser(invalidUser));

  assertEquals("Invalid email", exception.getMessage());
}

How to fix it in AssertJ

Code examples

Noncompliant code example

@Test
void testNoExceptionThrown() {
  try {
    userService.registerUser(validUser);
  } catch (ValidationException e) {
    fail("Should not have thrown any exception");
  }
}

Compliant solution

@Test
void testNoExceptionWithAssertion() {
  assertThatCode(() -> userService.registerUser(validUser)).doesNotThrowAnyException();
}

Noncompliant code example

@Test
void testExceptionIsThrown() {
  try {
    userService.registerUser(invalidUser);
    fail("Expected ValidationException to be thrown");
  } catch (ValidationException e) {
    assertThat(e).hasMessage("Invalid email");
  }
}

Compliant solution

@Test
void testExceptionWithAssertion() {
  assertThatThrownBy(() -> userService.registerUser(invalidUser))
    .isInstanceOf(ValidationException.class)
    .hasMessage("Invalid email");
}

Resources

Documentation