Анализ покрытия кода тестами для PHP проекта

2 мар. 2021 г.

Всем привет! Сегодня хотел бы рассказать о настройке анализа покрытия кода тестами для PHP(Symfony) проекта. И последующую интеграцию с Github Actions.

Анализ покрытия тестами нужен в первую очередь для определения того, насколько качественно написаны тесты и насколько они реально тестируют код. Подробнее об анализе покрытия кода.

Изначально настраивал для этого блога. Но затем, для большей наглядности, также настроил для одного из открытых проектов - placeholder-service.

Xdebug

Для начала нам понадобится 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-сервер.

...
![Code Coverage](https://raw.githubusercontent.com/antonshell/placeholder-service/master/.github/badges/coverage.svg)

Подключение к Github Actions

Настроим анализ покрытия тестами для 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"

Создание html отчета для каждого Pull Request

Также хотелось бы видеть сам отчет о покрытии тестами в каждом 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

Теперь нам нужно отображать сгенерированный отчет на странице 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 с реализацией покрытия тестами. На этом пока все. Спасибо за внимание!