#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'],
]
);
}