TestDox is a feature that allows tests to serve not only as executable verification but also as executable specification and executable documentation.
During test-driven development (TDD), tests are written before the production code. In this context, the tests serve as an executable specification: they describe the expected behaviour of the code that is yet to be written. Once the production code has been written, the tests continue to serve as executable documentation: they describe the actual behaviour of the code.
TestDox renders test method names as human-readable sentences. When used together with
the --testdox CLI option, it provides a behaviour-driven view of your test suite
that reads like documentation.
Default Behaviour
By default, PHPUnit uses the naming conventions of test classes and test methods to generate human-readable documentation.
Class Names
A test class name is prettified by removing the Test suffix, then splitting the camelCase name into words. For example, GreeterTest becomes Greeter.
When the test class is in a namespace, the unqualified class name is used as the title with
the fully qualified class name (without the Test suffix) in parentheses. For example,
App\Tests\GreeterTest becomes Greeter (App\Tests\Greeter).
Method Names
A test method name is prettified by removing the test prefix (or test_ for snake_case
method names), then converting the remainder to a sentence:
camelCase:
testGreetsWithNamebecomesGreets with namesnake_case:
test_greets_with_namebecomesGreets with nameNumbers:
testValueIsGreaterThan0becomesValue is greater than 0
Consider the following test class:
Example 8.1 A class named Greeter (declared in src/Greeter.php)
<?php declare(strict_types=1); final class Greeter { public function greet(string $name): string { return 'Hello, ' . $name . '!'; } public function greetWithTimeOfDay(string $name, int $hour): string { if ($hour < 12) { return 'Good morning, ' . $name . '!'; } if ($hour < 18) { return 'Good afternoon, ' . $name . '!'; } return 'Good evening, ' . $name . '!'; } }
Example 8.2 A test class named GreeterTest (declared in tests/GreeterTest.php)
<?php declare(strict_types=1); use PHPUnit\Framework\TestCase; final class GreeterTest extends TestCase { public function testGreetsWithName(): void { $greeter = new Greeter; $this->assertSame('Hello, World!', $greeter->greet('World')); } public function testGreetsWithGoodMorningBeforeNoon(): void { $greeter = new Greeter; $this->assertSame( 'Good morning, Alice!', $greeter->greetWithTimeOfDay('Alice', 9), ); } public function testGreetsWithGoodAfternoonAfterNoon(): void { $greeter = new Greeter; $this->assertSame( 'Good afternoon, Alice!', $greeter->greetWithTimeOfDay('Alice', 14), ); } public function testGreetsWithGoodEveningAfter6Pm(): void { $greeter = new Greeter; $this->assertSame( 'Good evening, Alice!', $greeter->greetWithTimeOfDay('Alice', 20), ); } }
Running this test with the --testdox option yields the output shown below:
$ ./tools/phpunit --no-progress --testdox tests/GreeterTest.php PHPUnit 12.5.0 by Sebastian Bergmann and contributors. Runtime: PHP 8.5.3 Time: 00:00.077, Memory: 10.00 MB Greeter ✔ Greets with name ✔ Greets with good morning before noon ✔ Greets with good afternoon after noon ✔ Greets with good evening after 6 pm OK (4 tests, 4 assertions)
Data Providers
When a test method uses a data provider, TestDox appends the data set information to the prettified method name:
For indexed data sets:
with data set #0For named data sets:
with "name"
Customizing Test Documentation
The default prettification is often sufficient, but there are cases where you want more control over the documentation text. PHPUnit provides three attributes for this purpose.
The TestDox Attribute
The TestDox(string $text) attribute can be used at the class level and/or the method
level to specify custom documentation text.
At the class level, it replaces the prettified class name:
Example 8.3 Using the TestDox attribute at the class level
<?php declare(strict_types=1); use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; #[TestDox('Greeting Service')] final class GreeterTest extends TestCase { #[TestDox('Greets a person by their name')] public function testGreetsWithName(): void { // ... } }
Running this test with TestDox output enabled yields:
Greeting Service ✔ Greets a person by their name
See TestDox for the attribute reference.
Using Placeholders with Data Providers
When the TestDox attribute is used on a test method that uses a
data provider, you can embed
placeholders that reference the method’s parameters. Each placeholder is the parameter
name prefixed with $.
Example 8.4 Using placeholders in the TestDox attribute
<?php declare(strict_types=1); use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\TestDox; use PHPUnit\Framework\TestCase; final class CalculatorTest extends TestCase { #[DataProvider('additionProvider')] #[TestDox('Adding $a to $b results in $expected')] public function testAdd(int $expected, int $a, int $b): void { $this->assertSame($expected, $a + $b); } public static function additionProvider(): array { return [ 'zero plus zero' => [0, 0, 0], 'zero plus one' => [1, 0, 1], 'one plus zero' => [1, 1, 0], 'one plus one' => [2, 1, 1], ]; } }
Running this test with TestDox output enabled yields:
Calculator ✔ Adding 0 to 0 results in 0 ✔ Adding 0 to 1 results in 1 ✔ Adding 1 to 0 results in 1 ✔ Adding 1 to 1 results in 2
When placeholders are used, data provider information is not appended to the text because the parameter values are already embedded in the documentation text.
The special placeholder $_dataName is available and resolves to the name of
the current data set (for example, zero plus zero or one plus one in the example
shown above).
The TestDoxFormatter Attribute
The TestDoxFormatter(string $methodName) attribute can be used on a test method to
specify a static method in the same class that formats the documentation text. This is
useful when the documentation text requires logic that cannot be expressed with simple
placeholders, for example when objects need to be formatted in a specific way.
The formatter method must be public and static. It receives the same parameters
as the test method (from the data provider) and must return a string.
Example 8.5 Using the TestDoxFormatter attribute
<?php declare(strict_types=1); use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\TestDoxFormatter; use PHPUnit\Framework\TestCase; final class ExampleTest extends TestCase { public static function provider(): array { return [ [ new DateTimeImmutable('2025-08-01'), new DateTimeImmutable('2025-08-01'), ], ]; } public static function formatter(DateTimeImmutable $expected, DateTimeImmutable $actual): string { return sprintf( '%s is expected to be %s', $actual->format('Y-m-d'), $expected->format('Y-m-d'), ); } #[DataProvider('provider')] #[TestDoxFormatter('formatter')] public function testOne(DateTimeImmutable $expected, DateTimeImmutable $actual): void { $this->assertEquals($expected, $actual); } }
Running this test with TestDox output enabled yields:
Example ✔ 2025-08-01 is expected to be 2025-08-01
See TestDoxFormatter for the attribute reference.
The TestDoxFormatterExternal Attribute
The TestDoxFormatterExternal(string $className, string $methodName) attribute works
like TestDoxFormatter, but references a public static method declared in another class.
This is useful when multiple test classes share the same formatting logic.
See TestDoxFormatterExternal for the attribute reference.
TestDox Output
Console Output
The --testdox CLI option replaces the default result output with TestDox format.
The following symbols are used to indicate the status of each test:
✔ Test passed
✘ Test failed or errored
↩ Test was skipped
⚠ Test triggered a warning
∅ Test is incomplete
The --testdox-summary CLI option repeats the TestDox output at the end of the test
run for tests that had errors, failures, or issues. This provides a focused summary of
problematic tests in TestDox format.
Plain Text Output
The --testdox-text <file> CLI option writes the test results in TestDox format as a
plain text file. Successful tests are marked with [x] and defective tests are marked
with [ ]:
Greeter [x] Greets with name [x] Greets with good morning before noon [x] Greets with good afternoon after noon [x] Greets with good evening after 6 pm
This can also be configured in the XML configuration file using the <testdoxText> element
inside the <logging> element (see The <testdoxText> Element).
HTML Output
The --testdox-html <file> CLI option writes the test results in TestDox format as an
HTML file. This produces a browsable document with styled results where successful tests
are marked with a green ✓ and defective tests are marked with a red ✗.
This can also be configured in the XML configuration file using the <testdoxHtml> element
inside the <logging> element (see The <testdoxHtml> Element).
XML Configuration
TestDox output can be enabled through the XML configuration file as an alternative to using CLI options.
The testdox attribute on the <phpunit> element enables TestDox console output
(see The testdox Attribute).
The testdoxSummary attribute on the <phpunit> element enables the TestDox summary
(see The testdoxSummary Attribute).
File-based TestDox output is configured using child elements of the <logging> element:
<phpunit testdox="true" testdoxSummary="true"> <logging> <testdoxHtml outputFile="testdox.html"/> <testdoxText outputFile="testdox.txt"/> </logging> </phpunit>