Покрытие кода тестами в PHP: метрики, инструменты и лайфхаки

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Парадокс тестировщика: 100% покрытие — не панацея

Разработчики делятся на два лагеря: те, кто пишет тесты «для галочки», и те, кто гоняется за заветными 100% покрытия кода. Истина, как всегда, посередине. В этой статье мы разберем, что такое покрытие кода тестами в PHP, какие метрики действительно важны, а какие могут ввести в заблуждение. Вы узнаете, как с помощью PHPUnit, Xdebug и Infection превратить тестирование из рутины в мощный инструмент контроля качества.

Давайте сразу договоримся: статья не про «как написать тест», а про то, как понять, что ваши тесты работают. Мы будем говорить на языке цифр, метрик и реальных примеров. Если вы пишете на PHP и хотите, чтобы ваш код не рассыпался при первом же изменении — вы по адресу.

Что такое покрытие кода (Code Coverage) на самом деле?

Покрытие кода — это метрика, показывающая, какой процент исходного кода выполняется во время прогона тестов. Звучит просто, но за этой цифрой скрываются разные уровни анализа:

  • Покрытие строк (Line Coverage) — самая базовая метрика. Сколько строк кода «затронуто» тестами.
  • Покрытие ветвлений (Branch Coverage) — проверяет, были ли выполнены все возможные ветки условий (if/else, switch).
  • Покрытие путей (Path Coverage) — самая сложная метрика. Проверяет все возможные маршруты выполнения кода.
  • Покрытие функций/методов — сколько функций было вызвано хотя бы один раз.

Для PHP-разработчика золотым стандартом считается покрытие строк + ветвлений. Но помните: 80% покрытия строк могут скрывать 40% непроверенных ветвлений. Код может быть «выполнен», но логика — не протестирована.

Почему 100% покрытие — это ловушка?

Представьте функцию, которая обрабатывает платежи:

<?php

function processPayment(float $amount, string $currency): bool {
    if ($amount <= 0) {
        throw new InvalidArgumentException('Сумма должна быть положительной');
    }
    
    $rate = getRate($currency); // внешний API
    $converted = $amount * $rate;
    
    if ($converted > 10000) {
        return false; // требуется подтверждение
    }
    
    return saveTransaction($converted); // запись в БД
}

Тест, который проверяет только успешный сценарий (сумма > 0, валюта USD, конвертация < 10000), даст вам 100% покрытия строк. Но он не проверит ни исключение, ни случай с большим платежом. Вывод: количество покрытых строк не равно качеству тестов.

Инструменты для измерения покрытия в PHP

Экосистема PHP предлагает несколько мощных инструментов. Рассмотрим три основных: Xdebug, PCOV и PHPUnit с встроенной поддержкой покрытия.

Xdebug: ветеран с широкими возможностями

Xdebug — самый популярный дебаггер и профайлер для PHP. Он умеет собирать данные о покрытии кода. Установка стандартная:

# Установка через pecl
pecl install xdebug

# Или через Docker
RUN pecl install xdebug && docker-php-ext-enable xdebug

В php.ini нужно включить режим покрытия:

xdebug.mode=coverage

Запуск с PHPUnit:

./vendor/bin/phpunit --coverage-html ./reports/coverage

Плюсы Xdebug: точность, поддержка ветвлений, интеграция с CI/CD. Минусы: замедляет выполнение тестов в 3-5 раз. Для больших проектов это критично.

PCOV: быстрый и легковесный

PCOV (PHP Code Coverage) — альтернатива Xdebug, созданная специально для сбора покрытия. Она не тормозит выполнение, так как не содержит дебаггера.

pecl install pcov

# Включение в php.ini
extension=pcov.so
pcov.enabled=1

Запуск аналогичен:

./vendor/bin/phpunit --coverage-html ./reports/coverage

PCOV подходит для CI/CD, где скорость важна. Но он не умеет собирать покрытие ветвлений — только строки. Для быстрых проверок — идеально.

PHPUnit: встроенные возможности

Начиная с версии 9, PHPUnit умеет работать с покрытием напрямую через драйверы (Xdebug или PCOV). Настройка минимальна. Пример конфигурации phpunit.xml:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit>
    <coverage>
        <include>
            <directory>src/</directory>
        </include>
        <report>
            <html outputDirectory="coverage/"/>
            <clover outputFile="coverage.xml"/>
        </report>
    </coverage>
</phpunit>

Теперь вы можете видеть не только общий процент, но и «дыры» в каждом файле. Отчет в HTML — это интерактивная карта вашего кода с подсветкой непокрытых строк.

Метрики, которые стоит отслеживать

Хорошая новость: вам не нужно гнаться за 100%. Плохая: нужно понимать, какие метрики реально отражают качество.

1. Покрытие строк (Line Coverage)

Базовый минимум. Если у вас меньше 50% — код практически не тестируется. Цель: 70-80% для production-проектов. Но помните про ловушку из примера выше.

2. Покрытие ветвлений (Branch Coverage)

Критически важная метрика для бизнес-логики. Если у вас много if/else, switch или тернарных операторов — следите за этим показателем. Хороший уровень: 60-70% ветвлений.

3. Покрытие мутаций (Mutation Score)

Это метрика из мутационного тестирования. Идея: в код вносятся мелкие изменения (мутации) — например, > меняется на >=, true на false. Если тест не падает — мутация выжила, а значит, тест не проверяет этот сценарий.

Инструмент Infection делает это автоматически. Установка:

composer require --dev infection/infection

Запуск:

./vendor/bin/infection --min-msi=                

Блоги

Рекомендаций по книгам