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.