jkstallard
jkstallard

Reputation: 395

Can i run php unit tests using Laravel Policy in route middleware?

I am using Laravel policies to control authorisation on my api routes. I want to only allow the current user to update their own post. This works fine when i manually run it through the application, but the unit tests fail. The unit tests, redirect to the login screen.

route:

Route::post('/posts/{post:reference}/editDetails', [PostDetailsApiController::class, 'update'])
   ->middleware('can:update,post');

policy:

public function update(User $user, Post $post)
{
   return $post->user_id === $user->id;
}

unit test:

$this->user = User::factory()->create();
$post = Post::factory()->create(['user_id' => $this->user->id]);
Passport::actingAs($this->user);
$response = $this->call('POST', 'http://test/api/posts/' . $post->reference . '/editDetails');
$response->assertStatus(200);

but this test fails saying 'Failed asserting that 200 is identical to 302'. If i add followingRedirects() and actingAsClient then it passes. If i dd in the controller, it doesnt get fired, so i'm pretty sure the controller isnt getting hit? if i remove the middleware, it runs fine. any advice welcomed. thanks

Upvotes: 0

Views: 529

Answers (1)

mrhn
mrhn

Reputation: 18976

This has nothing to do with the policy as the status code 302 is a redirect. The problem lies within this snippet.

$this->call('POST', 'http://test/api/posts/' . $post->reference . '/editDetails');

Normally you would call it with a relative URL.

$this->call('POST', 'api/posts/' . $post->reference . '/editDetails');

A better approach is to use the route helper for named routes. I have a example project, where i used this approach.

$this->call('POST', route('posts.edit-details', ['reference' => $post->reference]));

Remember to add a name to your route.

Route::post('/posts/{post:reference}/editDetails', [PostDetailsApiController::class, 'update'])
    ->middleware('can:update,post')
    ->name('posts.edit-details');

Upvotes: 2

Related Questions