Test Without Assertion
ID |
java.test_without_assertion |
Severity |
critical |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
assertion, reliability, testing |
Description
Reports a test method — JUnit 3/4/5, TestNG, AssertJ, FestAssert, Hamcrest or Mockito — whose body reaches no assertion or verification call.
Not reported, because the check is real but is not written as an assertion call: the fluent expectations of Spring’s MockMvc and WebTestClient (andExpect, expectStatus), a REST Assured ResponseSpecification handed to a shared request helper, an awaited condition (Awaitility.await(), awaitXxx, waitForXxx, waitUntilXxx) whose timeout fails the test, and JUnit 4’s @Test(expected = …). A @Disabled/@Ignore test is not reported either — it does not run at all, so "it can only ever pass" is simply untrue of it.
Rationale
A test with no assertion proves only that the code under test ran without throwing. It stays green through behavioural regressions, refactors that quietly break a contract, and dead code paths — every change looks safe because nothing in the test can ever fail on it.
@Test
public void computesOrderTotal() {
OrderCalculator calc = new OrderCalculator();
calc.computeTotal(order);
}
Remediation
State what the test expects, so a regression actually turns the test red.
@Test
public void computesOrderTotal() {
OrderCalculator calc = new OrderCalculator();
assertEquals(42, calc.computeTotal(order));
}
What is not reported
A call is followed into a same-file helper method it resolves to, so a shared private assertXxx(…) helper is not flagged. A Spring Boot contextLoads() sanity test needs no assertion of its own — reaching it is the check. A call this rule cannot resolve to any type, local method or library is treated as though it might be an assertion rather than reported as a guess. Assertions reached only through a method reference, a helper declared in another file, or a framework outside the supported set (Awaitility, EasyMock, JMock/JMockit, Rest-assured, RxJava, Selenide, Spring MVC Test’s ResultActions, Truth, WireMock) are not detected.
Two more shapes count as unresolvable rather than as evidence that nothing is checked:
-
invoking a functional interface the test was handed — a
Consumersupplied by a@MethodSource, invoked asverify.accept(result)— because the assertions are in the caller’s lambda; -
a call that resolves to the very method being analysed, which happens between two overloads of the same name and argument count, since only the name and the count are matched.
A body whose calls cannot all be resolved is left alone as a whole. An unqualified assertEquals(..) under several on-demand imports is one such call: it resolves to nothing at all, and a test containing it is not reported even though the assertion is plainly there.