Test Class Without Test
ID |
php.test_class_without_test |
Severity |
critical |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Code Smell |
Language |
Php |
Tags |
coverage, reliability, testing |
Description
Reports a class that presents itself as a PHPUnit fixture — it extends a TestCase base, or its
name follows the …Test convention — yet declares no test method. The class runs no test, so
whatever it was written to cover is not covered, and the gap is invisible because the file reads
as part of a passing suite.
Rationale
PHPUnit collects a fixture and then finds nothing in it to run. The suite still goes green, the coverage report still counts the class, and nobody is told that the checks the class was created for are missing. The usual causes are a migration that dropped the markers a test method needs, a class left behind after its tests moved elsewhere, and setup scaffolding that never got its test.
All three markers PHPUnit accepts count as a test, so a fixture written in any of the three styles is left alone:
<?php
class OrderShipmentTest extends TestCase // FLAW — a fixture that covers nothing
{
protected function setUp(): void
{
$this->subject = new OrderShipment();
}
private function anOrder(): Order
{
return new Order(1, 'pending');
}
}
class ShippingRatesTest extends TestCase // OK — named by the test convention
{
public function testRatesAreQuotedPerZone(): void
{
$this->assertSame(3, count($this->subject->rates()));
}
}
class PackagingTest extends TestCase // OK — marked by the attribute
{
#[Test]
public function ratesAreQuotedPerZone(): void
{
$this->assertTrue($this->subject->isPacked());
}
}
The following are not reported, because each is correct as it stands:
-
an
abstractclass, atraitand aninterface— PHPUnit collects none of them on its own; -
a class whose name ends in
TestCase, the convention for a shared base; -
a class that another class in the same file extends — the base holds the setup, the subclasses hold the tests;
-
a class deriving from a base this analysis cannot see, since an unseen base may hold the tests;
-
a class inheriting a test from a base declared in the same file;
-
a class whose only fixture signal is its name while it exposes a public property — the shape of a domain type that merely happens to be called
BloodTest.
Remediation
Write the test the class was created for, or delete the class if the behaviour it covered is
already tested elsewhere. When the class exists only to be extended, make it abstract or name it
…TestCase so both PHPUnit and the reader can see that it is a base and not a fixture.
<?php
class OrderShipmentTest extends TestCase
{
protected function setUp(): void
{
$this->subject = new OrderShipment();
}
public function testShippingAnOrderMarksItDispatched(): void
{
$this->subject->ship($this->anOrder());
$this->assertTrue($this->subject->isDispatched());
}
private function anOrder(): Order
{
return new Order(1, 'pending');
}
}
abstract class AbstractShippingTestCase extends TestCase
{
protected function subject(): OrderShipment
{
return new OrderShipment();
}
}