Test Class Name Suffix

ID

php.test_class_names_end_test

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Php

Tags

naming, reliability, testing

Description

Reports a PHPUnit fixture that declares test methods but whose name does not end in Test. Those tests never run.

Rationale

PHPUnit discovers a suite by file name suffix — *Test.php out of the box — and PSR-4 autoloading requires the file to be named after the class it declares. A fixture called CatalogSearch or PricingSpec therefore lives in a file the runner never opens: every case it holds is silently skipped. That is worse than a failing test, because nothing points at the gap — the suite reports success and the coverage report simply shows the covered code as uncovered.

<?php

class CatalogSearch extends TestCase // FLAW — CatalogSearch.php is never collected
{
    public function testSearchMatchesByTitle(): void
    {
        $this->assertCount(2, (new CatalogSearch())->byTitle('php'));
    }
}

class StockLevelsTests extends TestCase // FLAW — the runner collects "…Test", not "…Tests"
{
    public function testStockIsReserved(): void
    {
        $this->assertTrue((new Stock())->reserve('sku-1'));
    }
}

class CatalogSearchTest extends TestCase // OK
{
    public function testSearchMatchesByTitle(): void
    {
        $this->assertCount(2, (new CatalogSearch())->byTitle('php'));
    }
}

Only a class that both extends a TestCase base and declares at least one test method is reported. The inheritance is what makes it a fixture rather than a domain type, and requiring a test method keeps the finding actionable — it names cases that are actually being skipped — while leaving a test-free fixture to php.test_class_without_test, which is the defect there.

Not reported: an abstract class, a trait and an interface, none of which the runner collects; a class whose name ends in TestCase, the convention for a shared base; and a class another class in the same file extends, which is that same shape spelled out.

Remediation

Rename the class — and its file with it — so the name carries the suffix the runner collects.

<?php
// CatalogSearchTest.php

class CatalogSearchTest extends TestCase
{
    public function testSearchMatchesByTitle(): void
    {
        $this->assertCount(2, (new CatalogSearch())->byTitle('php'));
    }
}

If the class is a shared base rather than a suite, make it abstract or name it …TestCase, so both the runner and the reader can see that it holds no cases of its own.

Configuration

Property Default Meaning

suffixes

[Test]

The class-name suffixes the project’s runner collects. Set this to match the suffix configured in phpunit.xml when the suite has standardised on something else, such as Spec.

© 2026 Xygeni. All rights reserved