在 Laravel PHPUnit 中测试未经授权的用户限制

Testing unauthorized user restriction in Laravel PHPUnit

Laravel 版本 5.2

在我的项目中,role_id = 4 的用户具有管理员角色,可以管理用户。

我在AuthServiceProvider中定义了以下能力:

public function boot(GateContract $gate)
{
    $this->registerPolicies($gate);

    $gate->define('can-manage-users', function ($user)
    {
        return $user->role_id == 4;
    });
}

我已经在 UserController __construct 方法中使用了这个能力,如下所示:

public function __construct()
{
    $this->authorize('can-manage-users');
}

在 ExampleTest 中,我创建了两个测试来检查定义的授权是否有效。

管理员用户的第一个测试 role_id = 4。此测试通过。

public function testAdminCanManageUsers()
{
    $user = Auth::loginUsingId(1);
    $this->actingAs($user)
        ->visit('users')
        ->assertResponseOk();
}

第二个测试是针对另一个没有 role_id = 4 的用户。我已经尝试过响应状态 401 和 403。但是测试失败了:

public function testNonAdminCannotManageUsers()
{
    $user = Auth::loginUsingId(4);
    $this->actingAs($user)
        ->visit('users')
        ->assertResponseStatus(403);
}

失败消息的前几行如下:

A request to [http://localhost/users] failed. Received status code [403].

C:\wamp\www\laravel\blog\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\InteractsWithPages.php:196 C:\wamp\www\laravel\blog\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\InteractsWithPages.php:80 C:\wamp\www\laravel\blog\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\InteractsWithPages.php:61 C:\wamp\www\laravel\blog\tests\ExampleTest.php:33

Caused by exception 'Illuminate\Auth\Access\AuthorizationException' with message 'This action is unauthorized.' in C:\wamp\www\laravel\blog\vendor\laravel\framework\src\Illuminate\Auth\Access\HandlesAuthorization.php:28

我也试过使用'see'方法如下:

public function testNonAdminCannotManageUsers()
{
    $user = Auth::loginUsingId(4);
    $this->actingAs($user)
        ->visit('users')
        ->see('This action is unauthorized.');
}

但它也失败了。我究竟做错了什么?我怎样才能通过测试?

错误是调用了visit方法。 visit 方法在 InteractsWithPages 特性中。此方法调用 makeRequest 方法,后者又调用 assertPageLoaded 方法。此方法获取返回的状态代码,如果它获取的代码不是 200,它会捕获一个 PHPUnitException 并抛出一个带有消息

HttpException

"A request to [{$uri}] failed. Received status code [{$status}]."

这就是测试失败并显示上述消息的原因。

使用get方法代替visit方法可以顺利通过测试。例如:

public function testNonAdminCannotManageUsers()
{
    $user = App\User::where('role_id', '<>', 4)->first();

    $this->actingAs($user)
        ->get('users')
        ->assertResponseStatus(403);
}

此测试将通过并确认非管理员用户无法访问 url。

由于 Auth 中间件在默认情况下未经身份验证时会重定向到登录路由,您还可以执行以下测试:

public function testNonAdminCannotManageUsers()
{
    $user = Auth::loginUsingId(4);
    $this->actingAs($user)
        ->visit('users')
        ->assertRedirect('login');
}

至少从 Laravel 5.4 开始,您将需要使用 assertStatus(403) 方法。

public function testNonAdminCannotManageUsers()
{
    $user = Auth::loginUsingId(4);
    $this->actingAs($user)
        ->visit('users')
        ->assertStatus(403);
}