Laravel
February 13

Упрощенный подход к тестированию политик в Laravel

И тесты тоже стали немного быстрее.

Большинство тестов — это функциональные тесты. Нам нравится уверенность в том, что можно смоделировать весь жизненный цикл запроса и ответа. Это включает такие вещи, как авторизация и валидация.

Таким образом, если политика запрещала пользователя на основании какого-то условия, я устанавливал именно такого пользователя, делал обычный запрос и утверждал, что ответ будет 403 Forbidden. Это довольно хорошо работало, но в одном из проектов я столкнулся с проблемой, усложнившей этот подход.

Условие политики, которое нужно было проверить, также частично выполнялось в middleware, подключенном к маршруту. Поэтому я не мог быть уверен в том, что логика политики выполняется, поскольку middleware отклоняло её до того, как политика была оценена.

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

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

// внутри метода теста
$policyMock = $this->partialMock(SomePolicy::class);
$policyMock->expects('viewAny')->andReturnFalse();

// теперь выполним обычный тестовый запрос и assertForbidden

Это даёт уверенность в том, что контроллер подключён к правильному методу политики. Если кто-то удалит или изменит авторизацию политики, тест будет провален из-за отсутствующего ожидания имитации.

Затем, для актуальной логики политики, я могу написать простые тесты, создающие экземпляр политики и утверждающие логику метода напрямую:

$userWithoutTheRightPermissions = User::factory()->create(/* настройка здесь */);
$this->assertFalse((new SomePolicy)->viewAny($userWithoutTheRightPermissions));

Этим тестам не нужно задавать так много данных, и мне не нужно имитировать HTTP-запрос через фреймворк. Они быстрые, легкие, и их можно тестировать напрямую, не беспокоясь о middleware.

Необходимо уделить этому больше времени в проекте, прежде чем выносить окончательное суждение, но пока мне очень нравится этот подход. Он решает весьма специфическую проблему, когда происходит дублирование авторизации, навязанной middleware, и политиками.

Осваиваем Laravel