Unannotated Test Lifecycle Hook

ID

java.junit3_override_setup

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Java

Tags

best-practice, junit, testing

Description

Disabled by default. The rule reports that the hook "will never run", which is false whenever the overriding method calls it explicitly — the majority of real findings. Repairing it needs method-symbol resolution the rule does not have.

Reports a setUp or tearDown method in an annotation-driven test class — one that has JUnit 4 or JUnit 5 test methods — which carries no matching lifecycle annotation.

Rationale

JUnit 3 recognised setUp and tearDown by name and ran them around every test. JUnit 4 and JUnit 5 do not: they run whatever is annotated, and a method named setUp means nothing to them.

This makes the defect unusually quiet. The class still compiles, the test methods still run, and they pass — but they run against whatever state the fields happened to have, because the setup never executed. Nothing fails until a test starts depending on that setup, at which point the failure appears far from its cause.

public class LedgerBalanceTest {

  private LedgerBook book;

  public void setUp() {          // never called by JUnit 4 or JUnit 5
    book = new LedgerBook();
  }

  @Test
  public void balanceStartsAtZero() {
    assertEquals(0, book.balance());   // NullPointerException, not a clear failure
  }
}

Remediation

Annotate the hook with the lifecycle annotation of the JUnit version in use, or — if the method was never meant to be a hook — rename it so it does not read like one.

public class LedgerBalanceTest {

  private LedgerBook book;

  @BeforeEach                    // @Before on JUnit 4
  public void setUp() {
    book = new LedgerBook();
  }

  @Test
  public void balanceStartsAtZero() {
    assertEquals(0, book.balance());
  }
}

What is not reported

A class extending junit.framework.TestCase is left alone: JUnit 3 still drives the hooks by name there, so no annotation is wanted. Nor is a class without any test method, an overload taking arguments, a private method the framework could not call, or a method marked @Override that inherits its annotation from the parent it overrides.

© 2026 Xygeni. All rights reserved