Ускоряем PHPUnit Code Coverage с помощью PCOV

PCOV — это драйвер для PHP используемый в PHPUnit для code coverage, аналогичный Xdebug или PHPDBG. С помощью него можно собрать информацию о покрытых линиях кода (code coverage lines) во время тестирования.

Основным его преимуществом перед другими инструментами анализа покрытия кода является его скорость. Он работает быстрее и использует меньше ресурсов.

.wpj-jtoc.—jtoc-theme-basic-light.—jtoc-has-custom-styles {
—jtoc-numeration-suffix: «. «;
—jtoc-numeration-color: #adadad;
}

О PCOV

PCOV был создан Joe Watkins исключительно нацеленным на функциональность покрытия кода без дополнительных функций (профилирование, трассирование), присутствующих в других инструментах, таких как Xdebug и PHPDBG. Такой подход позволяет повысить производительность и ускорить тестирование.

Сравнение PCOV и Xdebug

PCOV и Xdebug — это два различных PHP инструмента, используемые преимущественно для разных задач. Однако, оба инструмента могут быть использованы для анализа покрытия кода при проведении тестирования через PHPUnit.

С PCOV мы можем собрать информацию про line coverage, тогда как с Xdebug нам доступно line, branch и path coverage. Подробнее про виды покрытия по ссылке.

PCOV обладает рядом преимуществ перед Xdebug:

  • Быстрее при Code Coverage в 2-4 раза.
  • Потребляет меньше ресурсов.

Из недостатков PCOV перед Xdebug можно выделить следующее:

  • Не поддерживет path coverage и branch coverage.

Влияние на производительность

PCOV при включенном состоянии влияет на производительность PHP. Влияние на производительность не такое заметное как у Xdebug, но при выполнении статического анализа или запуска линтера, вы с этим можете столкнуться.
Активируйте его только для code coverage в GitHub workflow с запуском тестов или во время выполнения Code Coverage.

Для активации PCOV перед запуском тестов, можно использовать следующую команду:

php -dpcov.enabled=1 -dpcov.directory=/var/www/html ./vendor/bin/phpunit --coverage-text

UPD: Ошибки в PHPUnit в режиме Code Coverage ниже 9.5.20 версии при использовании @runInSeparateProcess

При использовании PHPUnit версии ниже 9.5.20 в режиме Code Coverage с PCOV, если вы запустите PCOV непосредственно перед запуском Code Coverage, вы можете столкнуться с ошибками в тестах которые используют аннотацию @runInSeparateProcess(что означает запустить тест в отдельном процессе):

PHP Fatal error:  Uncaught SebastianBergmannCodeCoverageNoCodeCoverageDriverAvailableException: No code coverage driver available in /var/www/html/vendor/phpunit/php-code-coverage/src/Driver/Selector.php:53
Stack trace:
#0 Standard input code(423): SebastianBergmannCodeCoverageDriverSelector->forLineCoverage(Object(SebastianBergmannCodeCoverageFilter))
#1 Standard input code(1192): __phpunit_run_isolated_test()
#2 {main}
  thrown in /var/www/html/vendor/phpunit/php-code-coverage/src/Driver/Selector.php on line 53
Fatal error: Uncaught SebastianBergmannCodeCoverageNoCodeCoverageDriverAvailableException: No code coverage driver available in /var/www/html/vendor/phpunit/php-code-coverage/src/Driver/Selector.php:53
Stack trace:
#0 Standard input code(423): SebastianBergmannCodeCoverageDriverSelector->forLineCoverage(Object(SebastianBergmannCodeCoverageFilter))
#1 Standard input code(1192): __phpunit_run_isolated_test()
#2 {main}
  thrown in /var/www/html/vendor/phpunit/php-code-coverage/src/Driver/Selector.php on line 53

Эта ошибка говорит нам, что драйвер Code Coverage отсутстсует и пакет phpunit/php-code-coverage его не видит при запуске изолированного теста.

Встает вопрос: Почему PHPUnit не видит драйвер? ведь мы активируем его непосредственно перед запуском Code Coverage.

Ответ заключается в том, что в PHPUnit до 9.5.20 версии, флаги с которыми запускался PHP скрипт, не передавались дочерним процессам порожденным PHPUnit Code Coverage. А значит дочерние процессы запускаются с отключенным PCOV драйвером. Подробно вы можете ознакомиться с issue на GitHub.

Есть два решения:

  1. Обновить PHPUnit до 9.5.20, чтобы он включал обновленную версию Code Coverage пакета с исправленной проблемой.
  2. Если вы не можете по каким-то причинам обновиться до 9.5.20, то необходимо активировать PCOV в php.ini для всего сервера в котором вы запускаете Code Coverage.

Совместимость

PCOV и Xdebug перегружают одни и те же части движка PHP, в результате чего они не совместимы для работы вместе.
Если вам будет необхоимо использовать Xdebug для дебага или профилирования, тогда будет нужно отключить PCOV и наоборот.

Про тестовый стенд

Для тестов я использовал Laravel проект BookStack. Тестовый набор проекта имеет 950 tests, 4578 assertions и включает UnitTests и IntegrationTests. Этого набора более чем достаточно чтобы увидеть разницу в производительности между PCOV и Xdebug.

Все тесты проводились в GitHub Actions. BookStack уже содержит в себе настроенный изолированный workflow для тестов. От теста к тесту я менял coverage драйвер и версию PHP в настройках shivammathur/setup-php@v2 GitHub Action.

Каждый тест был выполнен 3 раза и для статьи были выбраны средние значения.

Время выполнения тестов приведено в минутах.

В тестах использовалась PCOV версии 1.0 на PHP 8.2 и 7.4

Коммит на котором проводились тесты 6e6f1133

Тест производительности на PHP 8.2

Давайте посмотрим на разницу в скорости и потребляемых ресурсах между PCOV и Xdebug на PHP 8.2.

PCOV

Во время тестов я получил следующие результаты

Time: 02:00, Memory: 184.50 MB

2 минтуы — довольно неплохо для 950 tests и 4578 asserts.

Тест производительности PCOV в режиме code coverage на PHP 8.2

Xdebug 3.2

Xdebug 3.2 был неплохо оптимизирован для снижения накладных расходов. Но время скорости выполнения у Xdebug получилость в 2 раза хуже чем у PCOV. Отличие в потреблении memory ~2mb что является незначительным показателем.

Time: 03:55, Memory: 186.50 MB
Тест производительности Xdebug 3.2 в режиме code coverage на PHP 8.2

Тест производительности на PHP 7.4

PHP 7.4 не поддерживается с 28 ноября 2022, но есть множество проектов которые еще не мигрировали на 8+ версии и для этих проектов следующие тесты могут быть полезны.

PCOV

В тестах на PHP 7.4 он показал тоже самое время выполнения как и на PHP 8.2, но значения потребления memory в 2 раза выше чем на PHP 8.2.

Отличие в потреблении memory в тестах производительности для PHP 7.4 связано с оптимизациями которые были проведены в PHP 8.* версих.

Time: 02:00, Memory: 347.00 MB
Производительность PCOV на PHP 7.4 против Xdebug

Xdebug 3.1

Xdebug 3.1 по сравнению с PCOV оказался в 2.5 раза медленее на PHP 7.4. Разница в memory опять незначительная ~6mb.

Time: 04:56, Memory: 353.00 MB
Производительность Xdebug 3.1 на PHP 7.4 против PCOV

Xdebug 2.9

Конечно я не рекомендую вам использовать Xdebug 2 версии, т.к. он очень медленный. Но давайте посмотрим какие результаты будут, в случае если вы все еще используете такую версию.

Тесты показали результат что Xdebug 2.9 в 4 раза медленее по сравнению с PCOV для PHP 7.4.

Time: 07:59, Memory: 355.00 MB
Производительность Xdebug 2.9 на PHP 7.4 против PCOV

Итог тестов

Итоговая таблица тестов по Code Coverage:

Code Coverage Driver PHP Time Memory
PCOV 8.2 02:00 184.50 MB
PCOV 7.4 02:00 347.00 MB
Xdebug 3.2 8.2 03:56 186.50 MB
Xdebug 3.1 7.4 04:56 353.00 MB
Xdebug 2.9 7.4 07:59 355.00 MB

Как вы можете видеть:

  • PCOV быстрее Xdebug 3.2 (PHP 8.2) на 197%
  • PCOV быстрее Xdebug 3.1 (PHP 7.4) на 247%
  • PCOV быстрее Xdebug 2.9 (PHP 7.4) на 400%
  • Xdebug 3.2 (PHP 8.2) быстрее Xdebug 3.1 (PHP 7.4) на 25%
  • Xdebug 3.1 быстрее Xdebug 2.9 (PHP 7.4) на 60%

Заключение

PCOV выигрывает у Xdebug в скорости выполнения тестов с code coverage в 2-4 раза. Это может быть несущественным показателем если вы используете unit tests время выполнения которых занимает считанные секунды.

Но что если ваш проект имеет так-же интеграционные тесты с набором 5000 tests, 40000 assertions, время выполнения которых занимает от 5 минут?

В этом случае разница в скорости в 2-4 раза между PCOV и Xdebug может быть существенной и это может повлиять на скорость CI/CD и частоту запуска тестов программистом а следовательно и на скорость разработки новых фич из-за фикса багов на позднем этапе.

Важно так-же помнить что PCOV как и Xdebug может влиять на производительность приложения. Активируйте его только для code coverage в тестовом GitHub workflow или непосредственно перед выполнением тестов.

Источник: WP-Yoda.com

Андрей Писаревский

Андрей Писаревский — Backend Team Lead в EPAM. Имею коммерческий опыт в программировании с 2010 года и экспертизу в полном цикле веб разработки: frontend, backend, QA, а так же server administration. Использую в работе: PHP, WordPress, Slim Framework, Linux, Docker, Agile.

%d такие блоггеры, как: