Загрузка...

Pest или Testo?

php

Недавно увидел Testo — новый фреймворк для тестов на PHP.

Если в проекте уже используются Pest-тесты, стоит ли смотреть в сторону Testo и тем более переезжать?

Короткий ответ: с работающего Pest-проекта переходить пока не нужно. Testo интересный, но это не замена Pest в один клик.

Что такое Testo

Testo — самостоятельный фреймворк для тестирования PHP. Он не работает поверх PHPUnit, а запускает тесты своим раннером.

Тест выглядит так:

use Testo\Assert;
use Testo\Test;

#[Test]
final class ActivityTest
{
    public function restoresSoftDeletedActivity(): void
    {
        $activity = Activity::factory()->create();
        $activity->delete();

        $activity->restore();

        Assert::false($activity->trashed());
    }
}

То есть обычный PHP-класс, методы и атрибут #[Test]. Не нужно наследоваться от TestCase.

У Testo есть плагины для data provider’ов, повторного запуска тестов, покрытия кода, бенчмарков, inline-тестов. Есть интеграции с Mockery и Infection. Конфигурация хранится в PHP-файле testo.php, а не в XML. Подробности есть в документации Testo и на Packagist.

Чем Testo отличается от PHPUnit

PHPUnit — главный и самый старый тестовый фреймворк в PHP-мире. Laravel из коробки использует PHPUnit, Pest тоже работает на его базе.

Классический PHPUnit-тест выглядит так:

use PHPUnit\Framework\TestCase;

final class ActivityTest extends TestCase
{
    public function testCanRestoreSoftDeletedActivity(): void
    {
        $activity = new Activity();
        $activity->delete();

        $activity->restore();

        $this->assertFalse($activity->trashed());
    }
}

Testo предлагает другой подход:

use Testo\Assert;
use Testo\Test;

#[Test]
final class ActivityTest
{
    public function canRestoreSoftDeletedActivity(): void
    {
        $activity = new Activity();
        $activity->delete();

        $activity->restore();

        Assert::false($activity->trashed());
    }
}

PHPUnit — большая зрелая экосистема с огромным количеством интеграций. Testo — отдельный молодой инструмент со своей архитектурой и набором плагинов.

А Pest тут где?

Pest — это удобный синтаксис поверх PHPUnit.

test('can restore soft deleted activity', function () {
    $activity = Activity::factory()->create();
    $activity->delete();

    $activity->restore();

    expect($activity->trashed())->toBeFalse();
});

Для Laravel это очень удобный вариант. Тесты короткие, читаются нормально, а вся база PHPUnit остаётся доступной.

Важно: Testo не поддерживает Pest-синтаксис из коробки. Такой код:

test('can restore soft deleted activity', function () {
    // ...
});

не получится просто запустить через Testo.

У Testo свой стиль: классы, методы, атрибуты и Assert.

Но миграция частично автоматизируется

Для перехода есть пакет testo/bridge-rector. Это набор правил для Rector, который умеет конвертировать тесты между PHPUnit, Pest и Testo.

Например, он может заменить Pest-проверки:

expect($activity->trashed())->toBeFalse();

на проверки Testo:

Assert::false($activity->trashed());

Для этого в rector.php подключается набор правил:

use Rector\Config\RectorConfig;
use Testo\Bridge\Rector\Set\TestoRectorSetList;

return RectorConfig::configure()
    ->withPaths([__DIR__ . '/tests'])
    ->withSets([
        TestoRectorSetList::PEST_TO_TESTO,
    ]);

Но есть важный нюанс. Pest-тест — это функция с замыканием, а Testo-тест — метод класса. Автоматически превратить:

test('can restore soft deleted activity', function () {
    // ...
});

в полноценный класс с методом нельзя без потери смысла или странных решений.

Поэтому мигратор конвертирует assertions, но структуру тестов из Pest в классы нужно будет привести вручную. Для PHPUnit ситуация лучше: классы и методы уже есть, поэтому переход на Testo автоматизируется заметно проще.

Инструмент полезный, но запускать его сразу на всём проекте не стоит. Сначала лучше выбрать одну папку с тестами, прогнать Rector, посмотреть diff и запустить полный набор тестов.

Нужно ли переходить с Pest на Testo

В обычном Laravel-проекте — нет.

Если у вас уже есть Pest:

  • тесты запускаются в CI;
  • используются Laravel factories, RefreshDatabase, actingAs();
  • команда понимает текущий код;
  • есть свои хелперы, datasets и моки;

то переезд даст много работы и мало пользы.

Даже с автоматической заменой assertions нужно проверить интеграцию с Laravel, миграции, database-тесты, coverage, отчёты CI, моки и запуск из PhpStorm.

Тесты нужны, чтобы ловить ошибки. Если во время миграции вы неделю ловите ошибки в самих тестах — задача начинает выглядеть сомнительно.

Когда Testo стоит попробовать

Testo можно взять для нового небольшого пакета или отдельного модуля с чистой бизнес-логикой.

Например:

  • расчёт скидок;
  • правила начисления бонусов;
  • конвертеры данных;
  • SDK-клиент;
  • библиотека, не завязанная на Laravel.

Там можно понять, нравится ли подход: насколько удобно писать тесты, запускать их локально, смотреть ошибки и работать с данными.

Если всё-таки переходить — как сделать это без боли

Не нужно переписывать все тесты одним махом.

Лучший путь такой:

  1. Оставить Pest и PHPUnit как есть.
  2. Выбрать небольшой изолированный модуль.
  3. Подключить testo/bridge-rector и прогнать его только по этому модулю.
  4. Вручную перевести функциональные Pest-тесты в классы и методы Testo.
  5. Запустить тесты локально и в CI.
  6. Посмотреть, удобно ли с ним жить хотя бы пару недель.

Если Testo реально оказался удобнее именно в вашем коде — можно использовать его в новых независимых пакетах. Старые Pest-тесты при этом трогать не нужно.

Вывод

Если вы используете Pest в Laravel-проекте — оставайтесь на Pest. Он даёт удобный синтаксис, дружит с Laravel и остаётся частью большой PHPUnit-экосистемы.

Testo стоит попробовать в новом небольшом проекте, если интересно посмотреть на тесты без PHPUnit и с более явной PHP-конфигурацией.

Миграция частично автоматизируется через Rector: assertions он переведёт, но Pest-функции в классы Testo придётся переносить вручную. Поэтому переход имеет смысл только если Testo решает вашу конкретную задачу, а не просто потому, что появился новый фреймворк.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *