Парадокс тестировщика: 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=