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.
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
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
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.
Now at this point, if you simply run
chances are, you will be given empty code coverage (unless your source files are inside
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 for
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
Also here's the content of
<?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.