有没有一种方法可以在Laravel内部充当用户而无需访问数据库?

时间:2019-06-05 18:18:26

标签: php laravel phpunit dingo-api

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

当前正在运行Laravel 5.8和PHPUnit 8.1.5。目前,我将从Laravel工厂生成的users对象保存到文本文件中。我能够将其传递给名为“ 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函数允许我也从文件中传入与我的模拟用户对象数据相对应的模拟许可权数据,但是相反,它是在数据库中访问以从许可权表中提取信息。

编辑:

所以我一直在玩模拟对象,并且在这里弄清楚了如何模拟原始控制器:

$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'));

我如何才能使第二个代码块命中模拟的控制器,而不是像它的构造方法中那样站起来一个真正的代码呢?

1 个答案:

答案 0 :(得分:1)

最后给出了答案,现在已解决。这是我为那些想知道的人做的:

$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。如您所见,这被称为“大父母阶层”。祝你好运!