Как смоделировать данный метод Laravel Auth facade

#laravel #testing #integration-testing #mockery #laravel-facade

#ларавель #тестирование #интеграция-тестирование #издевательство #laravel-facade #laravel

Вопрос:

Я хочу проверить, что Auth facade при вызове createUserProivder() метода возвращает моего пользовательского провайдера.

Проблема в том, что в следующем коде, с закомментированной частью, AuthManager по-прежнему является оригиналом, а не макетом. С раскомментированной частью я получаю сообщение об ошибке: MockeryExceptionBadMethodCallException : Method Mockery_2_Illuminate_Auth_AuthManager::validate() does not exist on this mock object

Я не знаю, как это проверить.

Я хочу протестировать пользовательское поведение guard, которое вызывает UserProvider при вызове validated() метода Guard, и по этой причине мне нужно смоделировать фасад Auth, потому что именно он возвращает User Provider.

 public function testUserIsAuthenticatedWhenUserProviderFindsCredentialsMatch()
    {
        $userId = Uuid::uuid();
        $user = new User($userId);
        $userProvider = new UserProvider($user);

//        $this->partialMock(AuthManager::class, function ($mock) use ($userProvider) {
//            $mock->shouldReceive('createUserProvider')
//                ->once()
//                ->andReturn($userProvider);
//        });

        Auth::shouldReceive('createUserProvider')
           ->once()
           ->andReturn($userProvider);

        $result = $this->app['auth']->validate(['dummy' => 123]);
  

Метод для тестирования:

 /**
     * @param array $credentials
     * @return bool
     */
    public function validate(array $credentials = []): bool
    {
        $this->user = $this->provider->retrieveByCredentials($credentials);

        return (bool)$this->user;
    }
  

Поставщик услуг:

 class LaravelServiceProvider extends AuthServiceProvider
{
    /**
     * Register any application authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        Auth::extend(
            'jwt',
            function ($app, $name, array $config) {
                $moduleConfig = $app['config'];

                return new JWTAuthGuard(
                    Auth::createUserProvider($config['provider']),
                    $this->app['request'],
                    new JWTHelper()
                );
            }
        );
    }
}
  

Ответ №1:

То, что вы создали смоделированный класс, не означает, что он автоматически заменяется в сервисном контейнере. Диспетчер аутентификации привязан как одноэлементный, поэтому вы можете обновить общий экземпляр в сервисном контейнере с помощью:

 $mock = $this->partialMock(AuthManager::class, function ($mock) use ($userProvider) {
            $mock->shouldReceive('createUserProvider')
                ->once()
                ->andReturn($userProvider);
        });

$this->app->instance('auth', $mock);

$result = $this->app['auth']->validate(['dummy' => 123]);

...
  

Комментарии:

1. Проблема в том, что перед тестированием создается экземпляр приложения, и поэтому все, что вы делаете после, не имеет значения. Я нашел исправление после долгой отладки

2. @JorgeeFG метод экземпляра заменяет привязку в приложении…

3. Да, я понял вас, но я хочу сказать, что приложение загружается перед тестированием. Итак, прежде чем вы сможете запустить макет. Мне пришлось издеваться над ним на уровне настройки теста, а не в самом тесте

Ответ №2:

После долгой отладки я нашел точку, где я смог это сделать:

 protected function getEnvironmentSetUp($app)
{
    $this->mockUserProvider($app);
}

protected function mockUserProvider($app)

{
    $userId = Uuid::uuid();
    $user = new User($userId);
    $userProvider = new UserProvider($user);

    $mock = Mockery::mock(AuthManager::class)->makePartial();
    $reflection = new ReflectionClass($mock);
    $reflection_property = $reflection->getProperty('app');
    $reflection_property->setAccessible(true);
    $reflection_property->setValue($mock, $app);

    $mock
        ->shouldReceive('createUserProvider')
        ->andReturn($userProvider);
    $app->instance('auth', $mock);
}
  

Однако другой способ — просто создать UserProvider для целей тестирования внутри каталога Tests:

 class TestUserProvider extends AuthServiceProvider
{
    /**
     * Register any application authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Auth::provider(
            'TestProvider',
            function ($app, array $config) {
                return new UserProvider();
            }
        );
    }
}
  

Затем в тестовом файле

 /**
 * Define environment setup.
 *
 * @param Application $app
 * @return void
 * @noinspection PhpMissingParamTypeInspection
 */
protected function getEnvironmentSetUp($app)
{
    // Setup default database to use sqlite :memory:
    $app['config']->set('auth.defaults.guard', 'jwt');
    $app['config']->set(
        'auth.guards',
        [
            'jwt' => ['driver' => 'jwt', 'provider' => 'users'],
            'jwt2' => ['driver' => 'jwt', 'provider' => 'users']
        ]
    );
    $app['config']->set(
        'auth.providers',
        [
            'users' => ['driver' => 'TestProvider'],
        ]
    );
}