2 мар. 2021 г.
Всем привет! Сегодня хотел бы рассказать о настройке анализа покрытия кода тестами для PHP(Symfony) проекта. И последующую интеграцию с Github Actions.
Анализ покрытия тестами нужен в первую очередь для определения того, насколько качественно написаны тесты и насколько они реально тестируют код. Подробнее об анализе покрытия кода.
Изначально настраивал для этого блога. Но затем, для большей наглядности, также настроил для одного из открытых проектов - placeholder-service.
Для начала нам понадобится Xdebug. Его можно установить по-разному, в зависимости от окружения. Установка в docker:
FROM php:8.0-fpm # ... # RUN pecl install xdebug RUN docker-php-ext-enable xdebug
Установка локально на Linux Ubuntu 20.04:
sudo apt install php8.0-xdebug
Чтобы сгенерировать отчет о покрытии тестами, нужно при запуске phpunit добавить дополнительные параметры.
./bin/phpunit --coverage-html coverage --coverage-clover coverage.xml
Для сохранения html отчета в папку coverage: --coverage-html coverage
.
Для сохранения xml отчета: --coverage-clover coverage.xml
.
Он нам понадобится для генерации значка о покрытии. Либо для загрузки статистики в сторонний сервис.
Чтобы не вводить каждый раз вручную, можно добавить скрипт в composer.json
. И запускать таким образом composer test
.
{ ... "scripts": { ... "test": [ "./bin/phpunit --coverage-html coverage --coverage-clover coverage.xml" ], ... } ... }
В результате получим примерно такой отчет. Здесь виден общий процент покрытия и статистика по каждому файлу. Также видно, какие конкретно строчки кода тестами не покрыты. Можно проанализировать и написать соответствующие тесты.
Для создания значка покрытия можно использовать библиотеку PHPCoverageBadge. Установка библиотеки:
composer require --dev jaschilz/php-coverage-badger
Генерация значка на основании статистики покрытия:
vendor/bin/php-coverage-badger coverage.xml .github/badges/coverage.svg
Чтобы не вводить каждый раз вручную, можно добавить скрипт в composer.json
. И запускать таким образом composer update-badges
.
{ ... "scripts": { ... "update-badges": [ "vendor/bin/php-coverage-badger coverage.xml .github/badges/coverage.svg" ], ... } ... }
Затем добавим значок в README.md
. В случае приватного репозитория этот способ не работает, но можно вынести картинку на web-сервер.
... 
Настроим анализ покрытия тестами для Github Actions. Таким образом, можно смотреть покрытие тестами для каждого Pull Request, а также проверять, что покрытие кода выше определенного процента. При установке PHP нужно добавить Xdebug:
# https://github.com/shivammathur/setup-php (community) - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} coverage: xdebug tools: composer:v2 extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, dom, filter, gd, iconv, json, mbstring, pdo, xdebug env: update: true
Для начала добавим проверку уровня покрытия в .github/workflows/tests.yml
.
Проверяем, что общее покрытие кода тестами больше или равно 40%.
Для проверки используем coverage.xml
файл, сгенерированный при запуске тестов.
- name: Check test coverage id: test-coverage uses: johanvanhelden/gha-clover-test-coverage-check@v1 with: percentage: "40" filename: "coverage.xml"
Также хотелось бы видеть сам отчет о покрытии тестами в каждом Pull Request. Для этого можно было бы попытаться использовать хранилище артефактов. Либо можно использовать сторонний сервис codecov.io (платный). Либо можно использовать собственный внешний Web-сервер.
Для private репозитория нужно обязательно защитить Web-сервер с помощью basic auth или другим способом!
## —— Upload tests coverage report to remote server - name: Extract branch name shell: bash run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" id: extract_branch - name: Print branch name shell: bash run: echo ${{ steps.extract_branch.outputs.branch }} - name: Create directories for reports uses: appleboy/ssh-action@master env: BRANCH: ${{ steps.extract_branch.outputs.branch }} FILES_PATH: ${{ secrets.GA_FILES_PATH }} with: envs: BRANCH,FILES_PATH host: ${{ secrets.REMOTE_HOST }} username: ${{ secrets.REMOTE_USER }} key: ${{ secrets.SERVER_SSH_KEY }} passphrase: '' script: mkdir -p $FILES_PATH/$BRANCH/coverage - name: Uploads reports to server uses: appleboy/scp-action@master with: host: ${{ secrets.REMOTE_HOST }} username: ${{ secrets.REMOTE_USER }} key: ${{ secrets.SERVER_SSH_KEY }} passphrase: '' rm: true source: "coverage" target: ${{ secrets.GA_FILES_PATH }}/${{ steps.extract_branch.outputs.branch }}/coverage
Извлекаем название ветки, подключаемся по SSH к Web-серверу и создаем нужную директорию.
Затем сохраняем html отчет в эту директорию. Пришлось добавить двойную вложенность /coverage/coverage
т.к. в противном случае удаляется все содержимое branch папки.
Также нужно добавить в хранилище Github Secrets переменную GA_FILES_PATH
.
Затем сохранить в нее путь до директории с файлами на Web-сервере, например: /var/www/files/placeholder-service
.
Также нужно добавить переменные REMOTE_HOST
, REMOTE_USER
, SERVER_SSH_KEY
для доступа к серверу по SSH.
Подробнее об этом в предыдущей статье или
в этом руководстве.
После успешного выполнения отчет будет доступен по ссылке: http://files.antonshell.me/github-actions/placeholder-service/configure_code_coverage/coverage/coverage.
Теперь нам нужно отображать сгенерированный отчет на странице Pull Request.
Сделать это напрямую не получится, однако мы можем добавить ссылку на сгенерированный отчет.
Для этого создадим новое workflow.github/workflows/pull_request_comments.yml
, которое будет добавлять комментарий со ссылкой на отчет.
name: Pull request comments on: pull_request: jobs: pull_request_comments: runs-on: ubuntu-20.04 steps: - name: Extract branch name shell: bash run: echo "##[set-output name=branch;]$(echo ${GITHUB_HEAD_REF})" id: extract_branch - name: Print branch name shell: bash run: echo ${{ steps.extract_branch.outputs.branch }} - name: Add test coverage report comment uses: mshick/add-pr-comment@v1 with: message: | Test coverage report: [http://private.antonshell.me/github-actions/antonshell/${{ steps.extract_branch.outputs.branch }}/coverage/coverage](http://private.antonshell.me/github-actions/antonshell/${{ steps.extract_branch.outputs.branch }}/coverage/coverage) repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token-user-login: 'github-actions[bot]' # The user.login for temporary GitHub tokens allow-repeats: false # This is the default
Тестовый проект доступен на github. Также есть Pull Request с реализацией покрытия тестами. На этом пока все. Спасибо за внимание!