15. Мутаційні тести. Як тестувати тести. Специфічні мутатори для Laravel

Відео версія
(YouTube - для зворотнього зв'язку)

Мутаційні тести

Пакет для мутаційних PHP тестів infection.github.io

Мутатори специфічні для Laravel (self-made) Важливо: я їх не підтримую і не відповідаю за кодстайл. Оформлюйте як open source, якщо хочете.

Мутаційні тести буквально змінюють код. Кожна зміна називається мутатором. Для кожної зміни, спричиненої кожним мутатором, запускаються всі тести. Якщо тести не пройшли, то вони не покрили цю частину коду.

Актуальна конфігурація infection (буде доповнюватися):

infection.json5
{
  "$schema": "vendor/infection/infection/resources/schema.json",
  "source": {
    "directories": [
      "Modules",
      "app/Services",
      "app/Traits"
    ],
    "excludes": [
      "Tests",
      "Database"
    ]
  },
  "logs": {
    "text": "infection.log"
  },
  "mutators": {
    "@default": true,
    "PublicVisibility": {
      "ignoreSourceCodeByRegex": [
        "public function asCommand.*",
        "public function asListener.*",
        "public function asJob.*",
        "public static function boot.*",
        "public function scope.*"
      ]
    },
    "ProtectedVisibility": {
      "ignoreSourceCodeByRegex": [
        "protected static function newFactory.*",
        "protected function slugSource.*"
      ]
    },
    "MethodCallRemoval": {
      "ignoreSourceCodeByRegex": [
        ".*select.*"
      ]
    },
    "Mutator\\Colection\\First\\LaravelCollectionFirst": true,
    "Mutator\\Colection\\Last\\LaravelCollectionLast": true,
    "Mutator\\Colection\\FirstOrFail\\LaravelCollectionFirstOrFail": true,
    "Mutator\\Eloquent\\OrderBy\\LaravelEloquentOrderBy": true,
    "Mutator\\Eloquent\\OrderByDesc\\LaravelEloquentOrderByDesc": true,
    "Mutator\\Eloquent\\FindOrFail\\LaravelEloquentFindOrFail": true,
  }
}

Мутаційні тести дуже навантажують систему. ДУЖЕ. Тому я запускаю їх лише для змін у коді та лише на девайсах розробників (pre-push git hook). Не раджу запускати їх як звичайний крок у CI/CD.

.husky/pre-push
docker exec -t bh-app ./vendor/bin/infection  \
--git-diff-filter= \
--git-diff-base=main

Тестування schedule подій

Раджу тестувати чи зареєструвались події за розкладом. Бо їх відсутність може принести багато поганих речей на проді. Рішення з коробки я не знайшов, тому написав таке. Якщо бачите в цьому мінуси дайте знати.

Modules/HealthCheck/Tests/Unit/HealthCheckServiceProviderTest.php
<?php

namespace Modules\HealthCheck\Tests\Unit;

use Illuminate\Console\Scheduling\Event;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Arr;
use Tests\TestCase;

class HealthCheckServiceProviderTest extends TestCase
{
    /**
     * @test
     */
    public function canRegisterHealthHeartbeatScheduleEvents()
    {
        $scheduleEvents = app()->make(Schedule::class)->events();
        $command = Arr::first(
            $scheduleEvents,
            fn (Event $event) => str_contains($event->command, 'health:schedule-check-heartbeat') &&
            $event->expression === '* * * * *'
        );
        $this->assertNotNull($command);
    }

    /**
     * @test
     */
    public function canRegisterHealthPruneModelEvents()
    {
        $scheduleEvents = app()->make(Schedule::class)->events();
        $command = Arr::first(
            $scheduleEvents,
            fn (Event $event) => str_contains(
                $event->command,
                "model:prune --model='Spatie\Health\Models\HealthCheckResultHistoryItem'"
            ) &&
            $event->expression === '0 0 * * *'
        );
        $this->assertNotNull($command);
    }
}