【问题标题】:How to mock a given method of Laravel Auth facade如何模拟 Laravel Auth 门面的给定方法
【发布时间】:2023-03-26 21:55:01
【问题描述】:

我想测试 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()
                );
            }
        );
    }
}

【问题讨论】:

    标签: laravel testing integration-testing mockery laravel-facade


    【解决方案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]);
    
    ...
    

    【讨论】:

    • 问题是在测试之前,应用程序是实例化的,所以你之后做什么都没关系。经过大量调试,我找到了解决方法
    • @JorgeeFG 实例方法替换了应用中的绑定...
    • 是的,我知道了,但我的意思是应用程序在测试前启动。所以在你可以运行模拟之前。我不得不在测试设置级别模拟它,而不是在测试本身
    【解决方案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);
    }
    

    但是另一种方法是在 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'],
            ]
        );
    }
    

    【讨论】:

      猜你喜欢
      • 2016-05-02
      • 2015-04-10
      • 1970-01-01
      • 2022-11-07
      • 2020-06-09
      • 1970-01-01
      • 1970-01-01
      • 2016-09-24
      • 1970-01-01
      相关资源
      最近更新 更多