vincent15000's avatar

Test an API route with authentication

Hello,

I have a route to store a new model, this route is only accessible to admin users.

With Laravel 11.

class BranchTest extends TestCase
{
    use RefreshDatabase;

    public User $user;

    public function setUp(): void
    {
        parent::setUp();

        $this->user = User::create([
            'name' => 'Vincent',
            'email' => '[email protected]',
            'password' => 'ksdlfgjsldfkg',
            'admin' => true,
        ]);
    }

    /** @test */
    public function admin_can_create_a_branch(): void
    {
        $response = $this->actingAs($this->user)->postJson('/api/branches', [
            'name' => 'Ma super branche',
        ]);

        $response->assertCreated();

        $response->assertDatabaseHas('branches', [
            'name' => 'Ma super branche',
        ]);    
    }
}

It returns a 500 error because the policy doesn't work.

public function create(User $user): bool
{
    return $user->isAdmin();
}

And the controller checks for authorization via Gate::authorize('create', Branch::class);.

It works fine when I login and store a new branch, but the test fails.

Can you help me understand why ?

Thanks for your help.

V

0 likes
7 replies
Sergiu17's avatar

Make sure user is admin

public function admin_can_create_a_branch(): void
{
	$this->assertTrue($this->user->isAdmin());
}

what the logs say? and why 500 error instead of 403?

1 like
vincent15000's avatar

@Sergiu17 Ok thank you, it was a problem with the mass assignment (should be automatically deactivated in test mode no ?), I have added the admin field in the fillable array and it works.

1 like
Sergiu17's avatar

@vincent15000

should be automatically deactivated in test mode no ?

I don't think this should be the case, let's say you have a controller AdminController

public function store()
{
	return User::create([
		//..
		'admin' => true
	]);
}

If fillable should be deactivated in test mode, then you test will be green, but you will never be able to create an admin user using AdminController@store

1 like
vincent15000's avatar

@Sergiu17 I thought I read this somewhere in the document, but I just searched once again for this and I didn't find it. Perhaps I have just dreamed about it. No problem ;).

martinbean's avatar
Level 80

@vincent15000 Chain withoutExceptionHandling to see the actual error being thrown:

$this
    ->actingAs($this->user)
    ->withoutExceptionHandling()
    ->postJson('/api/branches', [
        'name' => 'Ma super branche',
    ])
    ->assertValid()
    ->assertCreated();

$this->assertDatabaseHas('branches', [
    'name' => 'Ma super branche',
]);

Also, if you’re testing JSON responses and this is related to your earlier Sanctum question, then you should be specifying the guard when authenticating the user:

$this->actingAs($user, 'api')
1 like
vincent15000's avatar

@martinbean Ok thank you ... well it works with and without $this->actingAs($user, 'sanctum').

So why does it work with both ?

And if I use api, I get an error saying that the api guard is not defined.

martinbean's avatar

So why does it work with both ?

@vincent15000 Because Laravel just automatically sets the authenticated user for that test request. The user isn’t “logged in” because there’s no cookies or tokens being passed in a CLI-ran test. It’s just a convenience.

1 like

Please or to participate in this conversation.