Как обновить PHPUnit: Пошаговое руководство, ловушки и лучшие практики для современных PHP-проектов

Подробное практическое руководство по безопасному обновлению PHPUnit до актуальных версий (10/11) в PHP-проектах. Рассматриваются этапы подготовки, пошаговый процесс, разбор частых ошибок, инструменты для автоматизации миграции (Rector) и лучшие практики для поддержания тестов в актуальном состоянии.
Введение

PHPUnit остается бесспорным стандартом де-факто для модульного тестирования в экосистеме PHP. Однако с переходом на новые мажорные версии (особенно скачок с PHPUnit 8/9 на PHPUnit 10, а теперь и появление PHPUnit 11) процесс обновления может превратиться из рутинной задачи в источник головной боли. Устаревшие методы, измененные соглашения об именовании и новые архитектурные подходы требуют внимательного планирования. Эта статья — подробное руководство, которое поможет вам безопасно и эффективно обновить PHPUnit в вашем проекте, минимизировав риски и downtime.

Почему обновлять PHPUnit критически важно?

Откладывание обновления — это накопление технического долга, который со временем становится все дороже. Актуальная версия PHPUnit обеспечивает: совместимость с новыми версиями PHP (8.2, 8.3, 8.4), улучшенную производительность тестов, более чистый и выразительный синтаксис, лучшую интеграцию с современными инструментами (например, статическими анализаторами), актуальные исправления безопасности и, что немаловажно, доступ к документации и поддержке сообщества. Работа на устаревшей версии рано или поздно заблокирует обновление всего стека технологий проекта.

Подготовка к обновлению: Аудит и план

Прежде чем менять версию в `composer.json`, необходимо провести тщательную подготовку.

  • Аудит текущего состояния. Выполните команду `./vendor/bin/phpunit --version` и изучите `composer.json`. Определите текущую мажорную версию и все прямые зависимости, связанные с тестированием (например, `mockery/mockery`, `prophecy/prophecy`). Проверьте, поддерживают ли они целевую версию PHPUnit.
  • Изучите журнал изменений (CHANGELOG). Зайдите на официальный сайт phpunit.de или в репозиторий на GitHub. Внимательно изучите breaking changes для каждой мажорной версии между вашей текущей и целевой. Особое внимание уделите PHPUnit 10 и 11: именно в них произошли самые значительные очистки API.
  • Обеспечьте надежное покрытие тестами. Убедитесь, что ваш текущий test suite проходит успешно (`./vendor/bin/phpunit`). Высокое покрытие (не обязательно 100%, но достаточное для ключевой логики) — ваша главная страховка при обновлении. Если тестов мало, рассмотрите возможность их наполнения перед миграцией.
  • Создайте план отката. Используйте систему контроля версий (Git). Все изменения должны выполняться в отдельной feature-ветке. Убедитесь, что можете быстро вернуться к рабочему состоянию.
Пошаговый процесс обновления

Рекомендуется выполнять обновление последовательно, мажорная версия за мажорной версией, особенно если прыжок значительный (например, с PHPUnit 7 на 11). Это упростит локализацию ошибок.

Шаг 1: Обновление зависимостей в `composer.json`. Измените версию `phpunit/phpunit` на желаемую, используя оператор `^` (например, `"phpunit/phpunit": "^11.0"`). Одновременно обновите связанные пакеты, такие как `phpunit/php-code-coverage`. Для PHPUnit 10 и выше пакет `phpspec/prophecy-phpunit` более не требуется, его можно удалить, если не используется явно.

Шаг 2: Установка новых версий. Выполните `composer update phpunit/phpunit --with-dependencies`. Флаг `--with-dependencies` обновит и связанные пакеты. Внимательно изучите вывод Composer на предмет предупреждений о несовместимости.

Шаг 3: Запуск тестов и анализ ошибок. После обновления запустите тесты. Скорее всего, вы столкнетесь с ошибками. Не паникуйте — это ожидаемо. Систематизируйте их.

Типичные проблемы и их решения

  • Устаревшие аннотации -> Атрибуты. Это самое масштабное изменение в PHPUnit 10. Аннотации в комментариях `/** @test */`, `/** @covers */`, `/** @dataProvider */`, `/** @depends */` больше не поддерживаются. Их необходимо заменить на атрибуты PHP 8.
Было: `/** @test */ public function myTest() {}`  Стало: `#[Test] public function myTest() {}`
 Было: `/** @dataProvider dataProviderName */`
 Стало: `#[DataProvider('dataProviderName')]`
 Использование атрибутов делает код чище и является современным стандартом.

  • Изменение имен классов и методов. Многие методы, помеченные как устаревшие в 9-й версии, удалены в 10-й.
* `assertEquals()` с необязательными параметрами `$delta`, `$maxDepth`, `$canonicalize`, `$ignoreCase` — теперь нужно использовать специализированные методы: `assertEqualsWithDelta()`, `assertEqualsCanonicalizing()` и т.д.  *  `assertContains()` и `assertNotContains()` для проверки строк и массивов разделены: используйте `assertStringContainsString()` и `assertArrayContains()` (в PHPUnit 11 `assertArrayContains` был переименован в `assertContains` для массивов, что добавляет путаницы — внимательно читайте документацию!).
 *  Класс `PHPUnit\Framework\TestCase` — это единственный правильный класс для наследования. Устаревшие псевдонимы удалены.

  • Конфигурация XML (`phpunit.xml`). Схема конфигурации обновляется. Устаревшие атрибуты и элементы (например, `checkForUnintentionallyCoveredCode`, `verbose`) могут быть удалены или заменены. Лучший подход — сгенерировать свежий конфигурационный файл с помощью команды `./vendor/bin/phpunit --generate-configuration` и аккуратно перенести в него свои настройки.
  • Моки и стабы. Если вы используете Prophecy (который был встроен ранее), в PHPUnit 10+ его поддержка убрана из ядра. Вам нужно либо перейти на встроенный фреймворк моков PHPUnit (который значительно улучшился), либо явно установить `phpspec/prophecy-phpunit` и использовать соответствующий trait. Встроенный мок-билдер PHPUnit теперь предпочтительнее.
  • Кастомизация вывода. Классы, расширяющие `PHPUnit\TextUI\DefaultResultPrinter`, потребуют переработки, так как внутренняя архитектура вывода изменилась.
Автоматизация миграции

Для больших проектов ручная правка тысяч тестов нереальна. К счастью, существуют инструменты автоматизации.
*  Rector: Мощный инструмент для рефакторинга PHP-кода. Существуют готовые наборы правил (rulesets) для миграции с PHPUnit 9 на 10 и с 10 на 11. Установите Rector, настройте `rector.php` и запустите анализ — инструмент покажет, что можно исправить автоматически, и сделает это.
*  PHPUnit Migrations: Скрипты, предлагаемые самим PHPUnit (в репозитории есть папка `migrations`), но их возможности ограничены по сравнению с Rector.

Использование этих инструментов может сэкономить десятки часов работы, но после автоматического исправления обязателен тщательный ручной ревью изменений.

Лучшие практики после обновления

  • Обновите конвейер CI/CD. Убедитесь, что в ваших сценариях сборки в GitLab CI, GitHub Actions или Jenkins используется обновленная версия PHPUnit.
  • Приведите код к современному стилю. Используйте возможность заменить все аннотации на атрибуты. Это улучшит читаемость.
  • Рассмотрите возможность отказа от `setUp()` и `tearDown()` в пользу `setUpBeforeClass()`/`tearDownAfterClass()` или еще лучше — использования `#[Before]`, `#[After]`, `#[BeforeClass]`, `#[AfterClass]` атрибутов для более тонкого контроля.
  • Изучите новые возможности. Актуальные версии PHPUnit предлагают новые утверждения (assertions), улучшенную работу с исключениями, лучшую интеграцию. Потратьте время на изучение документации, чтобы писать более лаконичные и мощные тесты.
Заключение

Обновление PHPUnit — это инвестиция в стабильность, производительность и поддерживаемость вашего проекта. Хотя процесс требует времени и внимания, системный подход, использование инструментов автоматизации и следование лучшим практикам сведут риски к минимуму. Не откладывайте это на потом: регулярные обновления малыми шагами гораздо менее болезненны, чем один гигантский скачок через несколько лет. Современный PHPUnit — это быстрее, чище и надежнее, и он того стоит.
478 4

Комментарии (9)

avatar
4xuhawqqccmy 27.03.2026
Хороший обзор, но не хватает конкретных примеров кода для миграции аннотаций на атрибуты в PHPUnit 10+.
avatar
zr5694sksnf 29.03.2026
Автор, добавьте, пожалуйста, раздел про совместимость с другими пакетами для тестирования (Mockery, Prophecy). Это критично.
avatar
6v4jy6a9j 29.03.2026
Мне не хватило акцента на том, как убедить менеджмент выделить время на такое 'невидимое' обновление. Это проблема.
avatar
zai6sq3 29.03.2026
Статья полезная, но шаги для CI/CD-пайплайна (поэтапное обновление, откат) были бы ценным дополнением.
avatar
lebb0enk489 29.03.2026
Для небольших проектов обновление заняло 15 минут. Главное — заранее проверить депрекейшн-ворнинги в старой версии.
avatar
ma5gmi 30.03.2026
Спасибо за статью! Как раз планирую обновить PHPUnit 10->11 в большом легаси-проекте. Жду продолжения про ловушки.
avatar
9l92a04u 30.03.2026
Спасибо! Четкое руководство. Особенно оценил упоминание о --migrate-configuration для автоматического обновления конфига.
avatar
orvhe2an9 30.03.2026
Ловушка, с которой столкнулся: в PHPUnit 11 по умолчанию отключено преобразование ошибок в исключения. Сбило с толку!
avatar
naiykkx 30.03.2026
Обновил на прошлой неделе. Самое неприятное — это менять все dataProvider-методы с public на static в 10-й версии.
Вы просмотрели все комментарии