有没有办法在不访问数据库的情况下充当 Laravel 内的用户?

Is there a way to act as a user inside of Laravel without hitting the database?

我正在使用 PHPUnit 和 Laravel 为 API 编写单元测试。我正在测试的大多数功能都要求在功能 运行 之前对用户进行身份验证。用户数据存储在一个 table 中,他们的权限存储在另一个 table 中。我可以在 Laravel 中伪造用户对象,但我还需要能够从另一个 table 中提取相应的权限,而不必像 dingo 路由器当前正在做的那样访问数据库。

当前 运行 Laravel 5.8 和 PHPUnit 8.1.5。我目前将从 Laravel 工厂生成的用户对象保存到文本文件中。我能够将它传递给一个名为“actingAsApi”的函数(在 Github 上找到,下面的代码)并且允许我作为该用户进行身份验证。但是,该功能仍在运行并从数据库中获取该用户的所有权限。我正在尝试模拟或伪造它在某处拉动的权限对象,以便它根本不需要访问数据库。我还尝试使用 Passport::actingAs 的内置 Passport 函数,但这些函数也不起作用,因为它们仍在访问数据库(而且实际上并没有真正起作用)。

actingAsApi(在 TestCase.php 内)

protected function actingAsApi($user)
    {
        // mock service middleware
        $auth = Mockery::mock('Dingo\Api\Http\Middleware\Auth[handle]',
            [
                Mockery::mock('Dingo\Api\Routing\Router'),
                Mockery::mock('Dingo\Api\Auth\Auth'),
            ]);
        $auth->shouldReceive('handle')
            ->andReturnUsing(function ($request, \Closure $next) {
                return $next($request);
            });
        $this->app->instance('Dingo\Api\Http\Middleware\Auth', $auth);
        $auth = Mockery::mock('Dingo\Api\Auth\Auth[user]',
            [
                app('Dingo\Api\Routing\Router'),
                app('Illuminate\Container\Container'),
                [],
            ]);
        $auth->shouldReceive('user')
            ->andReturnUsing(function () use ($user) {
                return $user;
            });
        $this->app->instance('Dingo\Api\Auth\Auth', $auth);
        return $this;
    }

在我的测试文件中测试

public function testActAs() {
    $user = 'tests/users/user1.txt';
    $this->actingAsApi($user);

    $request = new Request;
    $t = new TestController($request);

    $test = $t->index($request);
}

我希望 actingAsApi 函数允许我也从文件中传入与我的模拟用户对象数据相对应的模拟权限数据,但它正在访问数据库以从权限中提取 table .

编辑:

所以我一直在玩模拟对象,我想出了如何在这里模拟原始控制器:

$controlMock = Mockery::mock('App\Http\Controllers\Controller', [$request])->makePartial();

$controlMock->shouldReceive('userHasPermission')
            ->with('API_ACCESS')
            ->andReturn(true);
                   
$this->app->instance('App\Http\Controllers\Controller', $controlMock);

但现在我无法弄清楚如何让其他控制器调用我来调用模拟控制器而不是真实控制器。这是我用于点击示例控制器的代码:

$info = $this->app->make('App\API\Controllers\InfoController');

print_r($info->getInfo('12345'));

我怎样才能让第二个代码块命中模拟控制器,而不是像在构造函数方法中那样建立一个真实的控制器?

终于有了答案,现在已经修复了。对于那些想知道的人,我是这样做的:

$request = new Request;

$controlMock = m::mock('App\API\Controllers\InfoController', [$request])->makePartial();
$controlMock->shouldReceive('userHasPermission')
             ->with('API_ACCESS')
             ->andReturn(true);

print_r($controlMock->getInfo('12345'));

基本上,我试图模拟原始的 API 控制器,然后捕获所有抛给它的调用。相反,我应该一直在模拟我正在测试的控制器,在本例中是 InfoController。然后我可以捕捉到调用 'userHasPermission',它应该联系到 Controller,但我会自动返回 true。这消除了访问数据库以接收权限和其他信息的需要。有关我如何使用 Mockery 解决它的更多信息,请参见此处:http://docs.mockery.io/en/latest/cookbook/big_parent_class.html。如您所见,这称为 'Big Parent Class'。祝你好运!