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 Consumer supplied by a @MethodSource, invoked as verify.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.

© 2026 Xygeni. All rights reserved