Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ashler2's avatar

Laravel Testings - All api calls 403 error.

I've got an application that i've started writing tests for. The auth was generated with Laravel UI, but the dashboard you get to is written in VUE.js. So i've needed to use the API.

I'm creating some tests but everytime i try to do this i get a 403 error for unauthorized. The only thing i can think of is something is missing from my request headers. Its worth noting that all the routes work correctly on the frontend and trying to access them via the api route shows the correct response with logged in or not/or has the correct permissions.

My api.php

Route::middleware('auth')->group(function () {
    Route::apiResource('users', UserController::class);
    Route::apiResource('roles', RoleController::class);
    Route::apiResource('permission-groups', PermissionGroupController::class);
    Route::apiResource('customers', CustomerController::class);
    Route::apiResource('packages', PackageController::class);
    Route::apiResource('projects', ProjectController::class);

    // Tasks
    Route::apiResource('tasks', TaskController::class);
    Route::patch('tasks/{task}/completed', [TaskController::class, 'complete'])->name('task.complete');
    Route::patch('tasks/{task}/order', [TaskController::class, 'order']);
    Route::apiResource('/tasks/{task}/notes', NoteController::class, ['except' => ['index', 'show', 'update']]);
    Route::patch('/tasks/{task}/status', [TaskController::class, 'status']);
});

My test case that fails -

class ExampleTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Test the api returns customers
     *
     * @test
     * @return void
     */
    public function api_gets_customers(): void
    {
        $this->login();
        $response = $this->getJson('api/customers');
        $response->assertStatus(200);
    }

The login function from testCase that the test extends. Its woth noting that all the permissions and roles are correct on the user from acting as


    protected function login(array $states = null)
    {
        $factory = User::factory();
        $this->actingAs($this->actor = $factory->create(), 'web');

        if (!$this->actor->roles->count()) {
            $this->actor->setRelation('roles', collect([
                (new Role(['name' => 'Test Role']))->setRelation('permissions', Permission::all())
            ]));
        }

        return $this;
    }

Is there something that i'm doing wrong to the login part that requires something extra to access the API?

0 likes
9 replies
tykus's avatar

Why are you making the Role and it's Permissions in-memory only? Have you tried actually creating the database records?

ashler2's avatar

@tykus Sorry forgot to include the part where the permissions are made as i don't think the issue is related to this. When i DD the user the permissions are on there correctly.

  • Code for the permissions/setup
    public function setUp(): void
    {
        parent::setUp();
        $this->seed(PermissionsSeeder::class);
    }

tykus's avatar

@ashler2 whenever the test request is made, the application will fetch a fresh instance of the User; this instance will not have any permissions.

ashler2's avatar

@tykus Yes it would seem its permission based, or at least the way the permissions are handled, but i'm not too sure of the issue yet. Keeping the route behind the auth permission but disabling the permission check in the controller seems to work correctly. Adding back in $this->authorize('Read Customers'); seems to error this again.

I'm not 100% sure what you mean with keeping this in the database, when i call the login function and DD the actor everything is present even after the request.

Does making the request even though the actor is set on the class create a new instance of the user? (I've not been able to find much on the docs regarding this)

tykus's avatar

@ashler2 everything is present even after the request everything is present on what exactly?

ashler2's avatar

@tykus The login function on the test class sets a class variable protected $actor;, So in the test if i dd($this->actor); I get the user with all the permissions/roles.

I definelty think this must be related to "spatie/laravel-permission" and how i've checked the permissions. It works on the frontend when logging into the site, but as the actor isn't it must be missing something or is an issue with the way i check the permissions.

tykus's avatar

@ashler2 yeah obviously, but what about the authenticated user in the context of the Request?

ashler2's avatar

@tykus I'm not sure i'm following here. If i var_dump the user in the test from $this->actor as we as dd in the controller for Auth::user() i get the same user if this is what you're asking.

tykus's avatar
tykus
Best Answer
Level 104

@ashler2 the variable in the test is not the Authenticated User in the Request. Just store the Role and Permissions so they are in the database to be queried whenever the Test Request is made.

Please or to participate in this conversation.