Запуск PHPUnit-тестів із code coverage у PhpStorm під час роботи в Docker

Запуск PHP Unit-тестов с code coverage в PhpStorm при работе в Docker

У PhpStorm є можливість створювати Run/Debug Configurations і запускати їх з IDE. Це дає змогу учасникам проєкту ділитися між собою спільними конфігураціями, використовуючи "Shared" прапор у налаштуваннях. Однією з таких операцій є запуск юніт тестів під час розробки. Поговоримо про те, як налаштувати цей процес із використанням Docker.

Є плагін PHPUnit code coverage для відображення покриття коду тестами в PhpStorm. PhpUnit можна запустити з опціями для генерації звіту з code coverage. У цьому звіті зберігатиметься інформація про кількість викликів кожного оператора під час виконання тестів. За цією статистикою можна вивести відсоток покриття коду тестами, адже якщо у файлі є оператори, які жодного разу не викликалися під час виконання тестів, значить, цей код тестами не покритий. Це і показує плагін у вигляді таблиці з навігацією по директоріях і файлах. Також плагін вміє підсвічувати зеленим і червоним кольором рядки у ваших файлах, які, відповідно, покриті або не покриті тестами:

PHPUnit подсветка текста

Щоб цей плагін працював коректно у PhpStorm з PHP CLI, налаштованому з Docker-контейнера, необхідна низка додаткових дій.

По-перше, наразі у PhpStorm не реалізовано коректно роботу з уже запущеними контейнерами. Це відомий недолік, обговорюється issueтут. Є issue, в якому користувачі пропонують варіанти вирішення, так само є issue, в якому планується фікс цієї проблеми. Раджу підписатися, щоб бути в курсі. Якщо коротко, то проблема в тому, що при налаштуванні запуску Run Configuration з Docker, PhpStorm перезапускає контейнер з PHP кожен раз, коли виконує в ньому скрипт, після чого зупиняє контейнер. Це можна побачити в результатах виконання запуску тестів із PhpStorm:

Testing started at 12:47 PM ...
docker-compose://[/home/user_name/sites/project_name/docker-compose.yml]:php_cli/php -dxdebug.coverage_enable=1 /app/vendor/phpunit/phpunit/phpunit --coverage-clover /opt/phpstorm-coverage/ProjectName$Run_all_tests.xml --configuration /app/phpunit.xml.dist --teamcity
Recreating project_name_php_cli_1 ...          <----- тут пересоздается контейнер
Attaching to project_name_php_cli_1
PHPUnit 7.3.1 by Sebastian Bergmann and contributors.
 
Recreating project_name_php_cli_1 ... done
 
Time: 7.9 seconds, Memory: 42.00MB
 
OK (689 tests, 1663 assertions)
 
Generating code coverage report in Clover XML format ... done
project_name_php_cli_1 exited with code 0
Aborting on container exit...                  <----- тут контейнер удаляется
 
Process finished with exit code 0

Такий підхід не дає можливості перебувати в перманентній розробці. Коли ви пишете тести і функціонал в IDE, потім запускаєте тести, після чого переключаєтеся в браузер і тестуєте функціонал вручну, вам необхідно щоразу запускати PHP контейнер вручну, оскільки PhpStorm зупиняє його після виконання вашого консольного скрипта з Run/Debug меню. Для обходу цього побічного ефекту, до моменту його виправлення з боку розробників JetBrains, ком'юніті придумало найпростіший варіант. Необхідно створити ще один PHP-контейнер, який буде використовуватися тільки для Run/Debug Configurations у PhpStorm. IDE буде його запускати, зупиняти, робити з ним, що хоче. При цьому ваш основний PHP-контейнер не буде вмирати, і ви зможете запускати свій код з веб-браузера (або іншого клієнта для роботи з HTTP-запитами). Конфігурація при цьому має такий вигляд:

docker-compose.yml

version: '2'
services:
    php:
        image: jekakm/php71-core:201807161
        environment:
            - "PHP_IDE_CONFIG=serverName=project-name-docker"
        volumes:
            - "./docker-configs/php-fpm.ini:/etc/php/7.1/fpm/php.ini"
            - "./docker-configs/php-cli.ini:/etc/php/7.1/cli/php.ini"
            - ".:/app:cached"
 
    # This container should be used for running php scripts inside PhpStorm.
    # Because PhpStorm recreates the php container every time it runs a script.
    # It is a temporary solution until it be fixed in new releases of PhpStorm.
    php_cli:
        build:
            context: ./docker-configs/php-image
        environment:
            - "PHP_IDE_CONFIG=serverName=project-name-docker"
        volumes:
            - "./docker-configs/php-cli.ini:/etc/php/7.1/cli/php.ini"
            - ".:/app:cached"

Примітка. Імейдж jekakm/php71-core:201807161 є нашим студійним імейджем для PHP контейнера. Ви можете взяти за основу будь-який інший імейдж для PHP.

Контейнер php_cli має успадковуватися від контейнера php або бути його копією. Залежно від конкретного PHP-імейджа і конкретної платформи, для коректної роботи code coverage плагіна необхідний root-доступ всередині контейнера. Ми цю проблему вирішили шляхом додаткової інструкції в нашому імейджі для консольного PHP. Якщо PhpStorm у звіті про запуск тестів із code coverage пише, що не може отримати доступ до певної директорії, тоді необхідно додати до файлу ./docker-configs/php-image/Dockerfile таке:

./docker-configs/php-image/Dockerfile

FROM jekakm/php71-core:201807161
USER root

Тепер потрібно налаштувати і вибрати PHP Server із Docker для наших скриптів із Run/Debug. Для цього в налаштуваннях шторму заходимо в "Languages & Frameworks → PHP → CLI Interpreter" і додаємо новий сервер. Також відмічаємо галочкою "Visible only for this project", бо під кожен проєкт у вас має бути свій docker-compose.yml, щоб підключення не шарилися між проєктами в штормі. Вибираємо конфігурацію для Docker Composer, далі з доступних сервісів у випадаючому меню Service вибираємо той, який ми створили - php_cli:

server

Також необхідно зайти в налаштування "Languages & Frameworks → PHP → Test Frameworks" і створити нову конфігурацію для "PHPUnit by Remote Interpreter". У випадаючому списку інтерпретаторів обираємо той, який ми налаштували кроком вище - PHP 7.1 Docker Compose. У вас є можливість конфігурувати налаштування PHPUnit за замовчуванням. Оскільки тести запускаються з Docker, нам необхідно вказати шляхи до файлів щодо їхнього розташування в контейнері. У нашому випадку проєкт знаходиться в папці /app, тому шлях до автолоадера вказано: /app/vendor/autoload.php, а шлях до конфігураційного файлу: /app/phpunit.xml.dist. Важливо правильно вказати цей шлях, оскільки шторм підказку вам не дасть.

settings

Також важливо зазначити, що для того, щоб шторм правильно обробив файл із результатом покриття, йому потрібно правильно вказати шлях до файлів. Для цього в налаштуваннях Test Frameworks для конфігурації, налаштованої на роботу з докером, необхідно додати нове правило в Path mappings. Локальний шлях має вказувати на директорію, в якій зберігається вихідний код у вас локально, у прикладі це /home/user/sites/project_name. Remote Path — це шлях до кореня вашого проєкту в докер контейнері, у прикладі це /app:

path

Це необхідно зробити, оскільки у звіті про покриття коду зберігаються шляхи до файлів, які обробив PHPUnit. Звіт, згенерований у Docker, відповідно, має шляхи до файлів щодо розташування в контейнері. І коли в штормі відкрито файл, який є у звіті, то шторм підсвічує в ньому покриття рядків. Якщо ж локальний шлях файлу і шлях файлу зі звіту не збігатимуться, то шторм просто не зрозуміє, що шлях із Docker-контейнера відповідає локальному шляху, і файл потрібно підсвітити.

Після всіх налаштувань перезбираємо контейнери, запускаємо їх, працюємо з проєктом у браузері, паралельно запускаємо тести в PhpStorm, які виконуються в контейнері php_cli і не чіпають контейнер php. Використання окремого контейнера для запуску консольних утиліт не обмежене одним лише PHPUnit. Тепер, маючи налаштований інтерпретатор із контейнера php_cli,ви можете створити інші Run/Debug Configurations у PhpStorm, наприклад, будь-який PHP Script, який також запускатиметься в цьому короткоживучому контейнері. Це дає змогу бачити результат виконання відразу в IDE без необхідності перемикатися в термінал і набирати команду вручну. А також це дає змогу оптимізувати розробку, адже на Run/Debug Configurations можна призначити гарячі клавіші в PhpStorm - і запуск рутинних завдань зводитиметься до конкретної комбінації клавіш на вашій клавіатурі.

Ще одним джерелом проблем у зв'язці PhpStorm + Docker може виступати утиліта docker-compose. Не забувайте її оновлювати. Сам зіткнувся з тим, що PhpStorm узагалі не хотів її запускати, оскільки в мене була встановлена занадто рання версія. Оновлення до останньої актуальної версії вирішило цю проблему. Перевірити наявність актуальних версій можна тут, знайти інструкції з оновлення docker-compose можна тут.

P.S. Якщо розробники JetBrains згодом доопрацюють поведінку роботи утиліти docker-compose у своїх IDE (так, ця проблема актуальна не тільки для PhpStorm), то потреба в наведеному вище обхідному способі відпаде.