I am a WordPress developer, and my daily tasks involves writing lots of PHP code. In my latest project, I am making a GraphQL server right inside WordPress and of course it involves testing.
For code-coverage, I use XDebug and while it works, it is painfully slow. In our GitLab CI, it takes over 30 minutes to run a complete coverage.
Enter PHP PCOV. After a complete setup, our tests finish in a little over 6 minutes. It is 5 times faster than before.
Now that you might feel convinced, let's dive in how we did that.
Step 1: Installation
We need the PCOV extension installed and XDebug extension completely disabled. To install PCOV run
pecl install pcov
The above would work on most Linux and MacOS systems. If you are using official PHP docker images, then run
pecl install pcov && docker-php-ext-enable pcov
Make sure to remove XDebug as well. On docker, simply don't install XDebug extension. On other system, edit php.ini
file and remove or comment the line with extension="xdebug.so"
.
Step 2: Clobber PHPUnit version 7 or less
PCOV has built-in support for PHPUnit version 8 or greater. But I am stuck with PHPUnit 7, because I need the full WordPress testing library which currently works only with PHPUnit 7. Luckily Joe Watkins the same person who made PCOV has also made a package called PCOV Clobber, for exactly this.
At this point, I am assuming you are using PHPUnit as a composer dependency. So run
composer require pcov/clobber --dev
followed by
vendor/bin/pcov clobber
This will make changes to the existing files inside vendor
directory to make XDebug drive use PCOV instead.
Note: You have to run this command every-time you update any composer package.
Step 3: Run PHPUnit with PCOV
Now at this point, if you simply run
./vendor/bin/phpunit
chances are, you will be given empty code coverage (unless your source files are inside src
, lib
or app
directory. So we have to make changes to some configuration variable while running PHPUnit. Simply run the following command and it should just work
php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude="~vendor~" ./vendor/bin/phpunit --coverage-text
Thanks to Ahmet Bora at this github issue to figure this out. Here's what's happening
-
pcov.enabled=1
- Enable PCOV support. -
pcov.directory=.
- Get coverage for all files in current directory. -
pcov.exclude="~vendor~"
- Exclude coverage forvendor
directory.
Bonus: GitLab CI Configuration
Since we run our tests mostly in CI, and we use GitLab, here's how our .gitlab-ci.yml
file looks like.
test:php73:
stage: test
tags:
- wordpress
image: registry.gitlab.com/wpquark/docker-containers/php-node:2.0.0-php-7.3-node-12.13.0
services:
- mariadb:10.3
before_script:
- yarn install --frozen-lockfile
- composer install
- vendor/bin/pcov clobber
script:
# - yarn phpunit
- php -dpcov.enabled=1 -dpcov.directory=. -dpcov.exclude="~vendor~" ./vendor/bin/phpunit --configuration=phpunit.ci.xml --testdox
artifacts:
paths:
- coverage/phpunit
expire_in: 2 days
The thing to notice is we do vendor/bin/pcov clobber
before running the phpunit
command.
Also here's the content of phpunit.ci.xml
<?xml version="1.0"?>
<phpunit
bootstrap="tests/bootstrap.php"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
verbose="true"
>
<testsuites>
<testsuite name="Unit Tests">
<directory suffix="Test.php">./tests/Unit/*</directory>
</testsuite>
<testsuite name="Integration Tests">
<directory suffix="Test.php">./tests/Integration/*</directory>
</testsuite>
<testsuite name="GraphQL Tests">
<directory suffix="Test.php">./tests/GraphQL/*</directory>
</testsuite>
</testsuites>
<filter>
<whitelist processUncoveredFilesFromWhitelist="false">
<directory suffix=".php">./inc</directory>
<exclude>
<directory suffix=".php">./inc/System</directory>
<directory suffix=".php">./inc/View</directory>
</exclude>
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"/>
<log type="coverage-clover" target="coverage/phpunit/clover.xml" showUncoveredFiles="true"/>
<log type="coverage-html" target="coverage/phpunit/html" lowUpperBound="35" highLowerBound="70"/>
</logging>
</phpunit>
I hope you will give PCOV a try. Also give a huge shout-out to @krakjoe who made all these things possible.
Top comments (1)
Two notes. XDEBUG_MODE=off can disable xdebug.
phpunit.xml can receive a range of input under a
<php>
section, which allows you to set ini values, like the ones you were setting with-d
.Great write-up.