Alternate names: missing-fail
When testing for exceptions in JUnit, it is easy to forget the call to fail():
try {
someOperationThatShouldThrow();
// forget to call Assert.fail()
} catch (SomeException expected) {
assertThat(expected).hasMessage("Operation failed");
}
This is better:
import static org.junit.Assert.fail;
try {
someOperationThatShouldThrow();
fail();
} catch (SomeException expected) {
assertThat(expected).hasMessage("Operation failed");
}
But using assertThrows is preferable and the least error prone:
import static org.junit.Assert.assertThrows;
SomeException expected =
assertThrows(SomeException.class, () -> someOperationThatShouldThrow());
assertThat(expected).hasMessage("Operation failed");
Without the call to fail(), the test is broken: it will pass if the exception
is never thrown or if the exception is thrown with the expected message.
If the try/catch block is defensive and the exception may not always be thrown, then the exception should be named ‘tolerated’.
This checker uses heuristics that identify as many occurrences of the problem as possible while keeping the false positive rate low (low single-digit percentages).
Five methods were explored to detect missing fail() calls, triggering if no
fail() is used in a try/catch statement within a JUnit test class:
assert*() method in the catch block.catch block.catch block is empty.try block contains only a single statement.Only the first three yield useful results and also required some more refinement
to reduce false positives. In addition, the checker does not trigger on comments
in the catch block due to implementation complexity.
To reduce false positives, no match is found if any of the following is true:
fail in its name is present in either catch or try block.throw statement or synonym (assertTrue(false), etc.) is present in
either catch or try block.setUp, tearDown, @Before, @After,
suite ormain method.try or catch block or immediately after.InterruptedException, AssertionError,
junit.framework.AssertionFailedError or Throwable.while(true) loop.try or catch block contains a continue; statement.try/catch statement also contains a finally statement.catch block.In addition, for occurrences which matched because they have a call to an
assert*() method in the catch block, no match is found if any of the following
characteristics are present:
assertTrue/False(boolean variable or field) in the catch block.try block is an assert*() (that is not a
noop): assertFalse(false), assertTrue(true)) or Mockito.verify() call.Suppress false positives by adding the suppression annotation @SuppressWarnings("MissingFail") to the enclosing element.