logo
podcast Podcast
get help Get Unstuck

A simpler approach to testing policies

And the tests are a little faster too

Joel Clermont
Joel Clermont
2024-08-13

Most of our tests are feature tests. We like the confidence of simulating the whole request and response lifecycle. This includes things like authorization and validation.

So, if a policy denied a user based on some condition, I would set up that exact kind of user, make the normal request, and assert the response was 403 Forbidden. It worked pretty well, but I ran into an issue on a project that made this approach more difficult.

The policy condition I wanted to test was also being partially enforced in a middleware attached to the route. So I couldn't actually be confident my policy logic was being enforced, since the middleware rejected it before the policy was evaluated.

I could remove the middleware dynamically using the withoutMiddleware helper, but now this is starting to feel like a messy test.

Instead, I tried a different approach. In one of my feature tests, I simply assert the policy is wired up to the right method:

// inside a test method
$policyMock = $this->partialMock(SomePolicy::class);
$policyMock->expects('viewAny')->andReturnFalse();

// now make the normal test request and assertForbidden 

This gives me confidence that the controller is wired up to the correct method on the policy. If someone deletes or changes that policy authorization, this test would now fail because of a missing mock expectation.

Then, for the actual policy logic, I can write very lightweight tests, which create a policy instance and assert the method logic directly:

$userWithoutTheRightPermissions = User::factory()->create(/* setup here */);

$this->assertFalse((new SomePolicy)->viewAny($userWithoutTheRightPermissions));

These tests don't need to set up quite as much data, and I don't need to simulate an HTTP request through the framework. They are fast, lightweight, and can be directly tested without worrying about middleware.

I need to spend more time with this on the project before making a final judgement, but so far, I really like the approach. It solves a very specific problem when there is overlap between authorization enforced by middleware and policies.

Here to help,

Joel

P.S. Check out the shiny new covers on our books, even the free ones!

Toss a coin in the jar if you found this helpful.
Want a tip like this in your inbox every weekday? Sign up below 👇🏼
email
No spam. Only real-world advice you can use.