Простые тесты конечных точек с 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
делает тестирование авторизации конечных точек ещё более простым. Это освобождает от необходимости сосредоточиться на том, что должно происходить в вашем приложении.
Не стесняйтесь копировать то, что я написал, и пробуйте. Если есть пожелания по улучшению, пишите в комментариях!