Test Class Without Test

ID

java.test_class_without_test

Severity

critical

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Code Smell

Language

Java

Tags

coverage, reliability, testing

Description

Reports a class that presents itself as a test fixture — a JUnit 3 TestCase, a JUnit 4 @RunWith fixture, a Spring @SpringBootTest, or a class named …​Test/…​Tests/…​TestCase — yet declares no test method.

Tests inherited from an abstract base class count as declared, wherever that base class lives: the one-line subclass of a shared base that holds every @Test is the commonest test-suite shape there is. So do tests declared in a nested class at any depth, which is how JUnit 5 @Nested groups are organised. Cucumber step definitions (io.cucumber.java8.En) are not reported either — their steps are lambdas registered in the constructor and are never methods.

Rationale

A fixture with no test contributes nothing, but it looks exactly like a passing test. Whatever the class was written to cover stays uncovered, and nobody notices, because the class runs green in every build. The usual cause is a test attribute dropped during a framework migration, or a fixture whose tests were moved out and never deleted along with it.

class OrderPricingTest {

  OrderPricingCalculator calculator = new OrderPricingCalculator();

  void setUpFixture() {
    calculator.reset();
  }
}

Remediation

Add the test method the class was meant to hold, or delete the class if its coverage moved elsewhere.

class OrderPricingTest {

  OrderPricingCalculator calculator = new OrderPricingCalculator();

  @Test
  void appliesBulkDiscount() {
    calculator.reset();
    assertEquals(90, calculator.priceFor(10, 100));
  }
}

What is not reported

An abstract class is left alone — it is designed for a subclass to extend, not to run on its own. A class whose only fixture signal is its name is left alone too when it declares a public field, since that is the shape of a domain type that happens to be named …​Test (a BloodTest), not of a fixture. A class that any other class in the analysed source extends is also left alone: a shared fixture base legitimately declares only setup, with the tests living in its subclasses — which normally sit in their own files.

An ArchUnit suite is not reported. ArchUnit states its checks as @ArchTest fields or methods rather than as @Test methods, so such a class declares no test method by construction; the @AnalyzeClasses marker its runner requires is enough on its own, since a suite may inherit or import every rule it runs.

The name has to read as <UnitUnderTest>Test. A class named nothing but the suffix — the TestCase parameterised-data holder — or one whose name ends in a purpose phrase such as …​HelpersForTest is not matched by the naming signal, although a @RunWith/@SpringBootTest marker on it still is.

Zohhak, Cucumber and Pact fixtures are not recognised, nor is TestNG’s class-level @Test (which implies every public method is a test), nor a test method inherited from an interface declared in another file.

© 2026 Xygeni. All rights reserved