我想测试 Auth 外观,当调用
createUserProivder()
方法时,返回我的用户提供程序。
问题是,对于以下代码,注释掉部分后,AuthManager 仍然是原始的,而不是模拟的。 对于未注释的部分,我收到错误:
Mockery\Exception\BadMethodCallException : Method Mockery_2_Illuminate_Auth_AuthManager::validate() does not exist on this mock object
我不知道如何测试。
我想测试一种自定义守卫行为,当调用 Guard 的
validated()
方法时,它会调用 UserProvider,因此我需要模拟 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()
);
}
);
}
}
仅仅因为您创建了一个模拟类,并不意味着它会在服务容器中自动替换。身份验证管理器绑定为单例,因此您可以使用以下方法更新服务容器中的共享实例:
$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]);
...
经过大量调试,我发现了一个可以做到这一点的点:
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);
}
然而,另一种方法是在 Tests 目录中创建一个用于测试目的的 UserProvider:
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'],
]
);
}
我知道这已经过时了,但如果这个解决方案有效,有人可能会受益或提供反馈。
我需要 Auth 外观来实现自定义规则:
public function passes($attribute, $value)
{
return Auth::user()->current_company === $value;
}
为了检查是否已通过,我们知道用户将返回用户模型或 \Illuminate\Contracts\Auth\Authenticatable 的实例。所以想为什么不像这样伪造外观:
public function testPasses()
{
$fake = new class {
function user() {
return new User(['pulse_current_company' => 1]);
}
};
Auth::swap($fake);
$this->assertTrue($this->getRule()->passes('company_id', 1));
}
看起来效果很好而且很简单。我知道这并不完全是嘲笑,它实际上使用的是假的,但对我有用。