Простые тесты конечных точек с Policy::fake

Источник: «Easier endpoint tests with Policy::fake»
Тестирование конечных точек может занять много времени, когда проверяется логика авторизации наряду с их поведением.

Тестирование конечных точек может занять много времени, когда проверяется логика авторизации наряду с их поведением. Для конечных точек, закрытых сложными политиками, требуется много настроек только для запуска метода контроллера. Всё становится ещё менее приятным, когда меняется политика, и ломается куча не связанных с ней тестов. Это отстой. Тесты конечных точек должны отвечать на вопрос Что это делает?, а не Когда я могу это использовать?.

В статье Упрощённый подход к тестированию политик в Laravelтелеграм ) Joel Clermont рассказал, что классы Laravel Policy можно подружить для решения этих проблем. Мне сразу понравилась эта идея, и я начал реализовывать её в тестах конечных точек.

Но я постоянно забывал, как это делается!

Класс AppPolicy

Чтобы облегчить запоминание этого подхода, я ввёл базовый класс AppPolicy для политик. Он предоставляет статический метод fake(), позволяющий с лёгкостью настраивать эти частичные имитации. Взгляните!

<?php

namespace App\Policies;

use Mockery\MockInterface;
use function Pest\Laravel\partialMock;

class AppPolicy
{
public static function fake(array $abilities): void
{
partialMock(static::class, function (MockInterface $mock) use ($abilities) {
foreach ($abilities as $ability => $result) {
$mock->shouldReceive($ability)
->andReturn($result)
->atLeast()
->once();
}
$mock->shouldIgnoreMissing();
});
}
}

Расширение AppPolicy в пользовательских политиках

Расширяя AppPolicy, пользовательские политики могут легко использовать метод fake:

class VideoPolicy extends AppPolicy
{
public function view($user, $video)
{
return $user->subscribed();
}
}

Это упрощает имитацию поведения политики в тестах без дублирования логики.

Имитация поведения Политики в тестах

Метод fake() может имитировать определённые методы политики во время тестирования:

VideoPolicy::fake([
'view' => true,
]);

Это гарантирует, что метод view всегда будет возвращать true в тесте, и тест завершится неудачей, если он не будет вызван, укрепляя правильную логику авторизации.

Примеры тестовых кейсов

Тест на запрет доступа

it('is forbidden if the user cannot view the video', function () {
VideoPolicy::fake(['view' => false]);

actingAs(User::factory()->create());
$video = Video::factory()->create();

$response = get(route('videos.show', $video));

$response->assertForbidden();
});

Тест на разрешённый доступ

it('is ok if the user can view the video', function () {
VideoPolicy::fake(['view' => true]);

actingAs(User::factory()->create());
$video = Video::factory()->create();

$response = get(route('videos.show', $video));

$response->assertOk();
});

Тестирование возможностей в ответах Inertia

С помощью Inertia также можно протестировать передачу возможностей (abilities) во фронтенд:

it('sends abilities to the frontend', function () {
VideoPolicy::fake([
'view' => true,
'edit' => true,
'delete' => false,
]);

actingAs(User::factory()->create());
$video = Video::factory()->create();

$response = get(route('videos.show', $video));

$response->assertInertia(fn (AssertableInertia $page) => $page
->where('can.view', true)
->where('can.edit', true)
->where('can.delete', false)
);
});

Преимущества

Использование Policy::fake делает тестирование авторизации конечных точек ещё более простым. Это освобождает от необходимости сосредоточиться на том, что должно происходить в вашем приложении.

Не стесняйтесь копировать то, что я написал, и пробуйте. Если есть пожелания по улучшению, пишите в комментариях!

Комментарии


Дополнительные материалы

Предыдущая Статья

Три подхода к селектору & (амперсанд) в CSS

Следующая Статья

Модернизация с Web-платформой: Производительность изображений