Document your app with PHPUnit Testdox output
When you run your application’s test suite (you do have a test suite, right?) with PHPUnit, you will probably be familiar with the following output style:
PHPUnit 9.5.26 by Sebastian Bergmann and contributors.
Testing
..........................................R.................R.... 65 / 90 ( 72%)
......................... 90 / 90 (100%)
Time: 00:05.650, Memory: 155.00 MB
The main advantage of this output format is that it is concise, especially when you have a lot of tests. It quickly gives you an overview of tests that failed, have errors, or are considered risky.
However, the downside is that it’s not immediately clear what each test is actually testing. Each test is simply
represented by a .
character and that is all.
There is an alternative that many people do not seem to know about. PHPUnit has an option to change the output format to one that is much more descriptive, and that relies on the information already in your tests to create these descriptions.
Lets say you have a test class such as the following:
<?php
declare(strict_types=1);
use PHPUnitFrameworkTestCase;
final class CustomerSubscriptionsTest extends TestCase
{
public function test_customer_can_be_subscribed_to_the_mailing_list(): void
{
// test code
}
public function test_customer_can_be_unsubscribed_from_the_mailing_list(): void
{
// test code
}
public function test_customer_cannot_be_subscribed_more_than_once(): void
{
// test code
}
public function test_customer_cannot_unsubscribe_if_not_already_subscribed(): void
{
// test code
}
}
If you add the --testdox
option to the PHPUnit command, you will get an alternative output format which looks like
this:
PHPUnit 9.5.26 by Sebastian Bergmann and contributors.
Testing /var/www/app/tests
Customer Subscriptions
✔ Customer can be subscribed to the mailing list
✔ Customer can be unsubscribed from the mailing list
✔ Customer cannot be subscribed more than once
✔ Customer cannot unsubscribe if not already subscribed
The test descriptions are taken from the method names inside the test class. If you name your methods carefully, using natural language, you are creating documentation right there and then, without any further effort.
Overriding the testdox description
If you would like the name your method something slightly different to the outputted description, you can override
it with the @testdox
annotation:
/**
* @testdox Customer can be subscribed OK
*/
public function test_customer_can_be_subscribed_to_the_mailing_list(): void
{
// test code
}
Using testdox with data providers
If you use data provider methods to run your test against multiple data sets, you can get the values from the data provider into the testdox description as well.
/**
* @dataProvider additionProvider
* @testdox Adding $a to $b results in $expected
*/
public function test_can_add_a_to_b_successfully($a, $b, $expected)
{
// test code
}
Additionally, if you use named sets in your data provider, you can access the set name in the testdox annotation using
$_dataName
as follows:
/**
* @dataProvider additionProvider
* @testdox Adding $a to $b results in $expected for $_dataName
*/
public function testAdd($a, $b, $expected)
{
// test code
}
public function additionProvider()
{
return [
'data set 1' => [0, 0, 0],
'data set 2' => [0, 1, 1],
'data set 3' => [1, 0, 1],
'data set 4' => [1, 1, 3]
];
}
This would result in the output for the first data set of Adding 0 to 0 results in 0 for data set 1
Conclusion
Once you start playing around with testdox output, you will probably realise how slack you’ve been when naming your test methods. It gets addictive, and you’ll find yourself tweaking things until you get the perfect desired output!
However, this time is well spent, as all one needs to do to get a summary of the behaviour of a part of your
application is to run the test suite with the --testdox
option, and the tests themselves will describe the desired
behaviour.