0

Mocking with the container — $this->mock(), $this->spy()

Intermediate5 min read·lv-26-005

Concept

Testing authentication and authorization verifies that routes are properly protected, roles are enforced, and unauthorized actions are rejected.

actingAs($user): Set the authenticated user for the request. No actual login flow — just sets the guard. $this->actingAs($user, 'api') for a specific guard.

be($user): Alias for actingAs().

assertAuthenticated(): Assert the current user is authenticated (after a login action). assertAuthenticatedAs($user).

assertGuest(): Assert not authenticated.

Testing login flow: Make a POST to the login route with credentials. Assert the user ends up authenticated and redirected.

Testing authorization:

  • Routes protected by auth middleware should return 401 (or redirect to login for web) when no user.
  • Routes protected by a Gate or Policy should return 403 when authenticated but unauthorized.
  • ->assertForbidden() checks for 403.

Gate::define() in test: You can override gates in a specific test: Gate::define('edit-post', fn($user) => $user->isAdmin()).

User factory with roles: User::factory()->admin()->create() if you have an admin state.

withoutMiddleware(): Bypass specific middleware (e.g., throttle or CSRF) in tests. Not for auth middleware — test auth properly.

Code Example

php
<?php
namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
use App\Models\User;
use App\Models\Post;

class AuthorizationTest extends TestCase
{
    use RefreshDatabase;

    // Auth middleware protection
    public function test_guests_cannot_access_dashboard(): void
    {
        $this->get('/dashboard')->assertRedirect('/login');
        $this->getJson('/api/user')->assertUnauthorized(); // 401
    }

    // Authenticated, wrong role
    public function test_regular_user_cannot_access_admin(): void
    {
        $user = User::factory()->create(['role' => 'user']);
        $this->actingAs($user)->get('/admin')->assertForbidden(); // 403
    }

    // Admin can access
    public function test_admin_can_access_admin_panel(): void
    {
        $admin = User::factory()->admin()->create();
        $this->actingAs($admin)->get('/admin')->assertOk();
    }

    // Policy: user can edit own post
    public function test_user_can_edit_own_post(): void
    {
        $user = User::factory()->create();
        $post = Post::factory()->for($user)->create();

        $this->actingAs($user)
             ->patch("/posts/{$post->id}", ['title' => 'New Title', 'body' => 'body'])
             ->assertRedirect();
    }

    // Policy: user cannot edit other's post
    public function test_user_cannot_edit_others_post(): void
    {
        $author = User::factory()->create();
        $other  = User::factory()->create();
        $post   = Post::factory()->for($author)->create();

        $this->actingAs($other)
             ->patch("/posts/{$post->id}", ['title' => 'Hack', 'body' => 'body'])
             ->assertForbidden();
    }

    // Login flow
    public function test_user_can_login_with_correct_credentials(): void
    {
        $user = User::factory()->create(['password' => bcrypt('secret')]);

        $this->post('/login', ['email' => $user->email, 'password' => 'secret'])
             ->assertRedirect('/dashboard');

        $this->assertAuthenticatedAs($user);
    }

    public function test_invalid_credentials_rejected(): void
    {
        $user = User::factory()->create(['password' => bcrypt('secret')]);

        $this->post('/login', ['email' => $user->email, 'password' => 'wrong'])
             ->assertSessionHasErrors(['email']);

        $this->assertGuest();
    }
}