【问题标题】:How to unit test all code execution paths of Laravel's Cache::remember functionality?如何对 Laravel 的 Cache::remember 功能的所有代码执行路径进行单元测试?
【发布时间】:2019-09-07 13:48:51
【问题描述】:

我是 Laravel 的 Cache::remember 功能的忠实粉丝,我在我的服务类中使用它,如下所示:

/**
 * SummaryService
 */
public function getSummaryData(string $userId)
{
    $summaryCacheKey = $userId . '_summary_cache';
    $summaryCacheLifespanMinutes = config('summary_cache_lifespan_minutes');

    return Cache::remember($summaryCacheKey, $summaryCacheLifespanMinutes, function () use ($userId) {

        $summaryResult = [
            'userExists' => false,
            'data' => [],
        ];

        $user = $this->userRepository->findById($userId);

        if ($user) {

            $summaryResult = [
                'userExists' => true,
                'data' => $this->summaryRepository->getSummaryByUserId($user->id),
            ];

        }

        return $summaryResult;

    });
}

这按预期工作。如果缓存中存在数据,则返回;如果不存在,则加载并缓存并返回。

现在,我正在尝试对我的SummaryService(两个执行路径)进行单元测试。

通过缓存返回数据的第一部分很容易测试,如下所示:

public function i_can_load_summary_data_via_cache()
{
    // given
    $userId = 'aaaa45-bbbb-cccc-ddddssswwwdw';

    $expectedResult = [
        'userExists' => true,
        'data' => [ ... ],
    ];

    $summaryCacheKey = $userId . '_summary_cache';
    $summaryCacheLifespanMinutes = config('summary_cache_lifespan_minutes');

    Cache::shouldReceive('remember')
        ->once()
        ->with($summaryCacheKey, $summaryCacheLifespanMinutes, Closure::class)
        ->andReturn($expectedResult);

    // when
    $result = $this->summaryService->getSummaryData($userId);

    // then
    $this->assertSame($expectedResult, $result);
}

但是,当我尝试测试数据不存在于缓存中的场景时,我必须像这样(通过模拟存储库)加载它:

public function i_can_load_summary_data_via_database()
{
    // given
    $userId = 'aaaa45-bbbb-cccc-ddddssswwwdw';

    $expectedResult = [
        'userExists' => true,
        'data' => [ ... ],
    ];

    $user = new User();
    $user->id = $userId;

    $summaryCacheKey = $userId . '_summary_cache';
    $summaryCacheLifespanMinutes = 0;

    Cache::shouldReceive('remember')
        ->once()
        ->with($summaryCacheKey, $summaryCacheLifespanMinutes, \Mockery::on(function() use($user) {
            $this->mockedUserRepository
                ->shouldReceive('findById')
                ->once()
                ->andReturn($user);
            $this->mockedSummaryRepository
                ->shouldReceive('getSummaryByUserId')
                ->once()
                ->with($user->id)
                ->andReturn([ ... ]);
        }))
        ->andReturn($expectedResult);

    // when
    $result = $this->summaryService->getSummaryData($userId);

    // then
    $this->assertSame($expectedResult, $result);
}

测试失败:

找不到匹配的处理程序 Mockery_3_Illuminate_Cache_CacheManager::remember('aaaa45-bbbb-cccc-ddddssswwwdw_summary_cache', '10', object(Closure))。该方法是意外的或其 参数与此方法的预期参数列表不匹配

对象:(数组('闭包'=>数组( '类' => '关闭', '属性' => 大批 ( ), ), ))

知道如何正确测试吗?

【问题讨论】:

    标签: php laravel unit-testing phpunit mockery


    【解决方案1】:

    好吧,我似乎把这个复杂化了;所以我已经把它分解了,并以稍微不同的方式修复它。

    我的服务代码现在如下所示:

    /**
     * SummaryService
     */
    
    public function getSummaryData(string $userId)
    {
        $summaryCacheKey = $userId . '_summary_cache';
        $summaryCacheLifespanMinutes = config('summary_cache_lifespan_minutes');
    
        return Cache::remember($summaryCacheKey, $summaryCacheLifespanMinutes, function () use ($userId) {
            return $this->loadLiveSummaryData($userId);
        });
    }
    
    public function loadLiveSummaryData(string $userId)
    {
        $summaryResult = [
            'userExists' => false,
            'data' => [],
        ];
    
        $user = $this->userRepository->findById($userId);
    
        if ($user) {
    
            $summaryResult = [
                'userExists' => true,
                'data' => $this->summaryRepository->getSummaryByUserId($user->id),
            ];
    
        }
    
        return $summaryResult;
    }
    

    现在,我只需要通过我的单元测试确认:

    1. 我的服务可以加载缓存版本并匹配调用参数
    2. 我可以加载实时数据(我可以在其中模拟存储库)

    看起来像这样:

    /**
     * @test
     */
    public function i_can_load_live_summary_data_for_existing_user()
    {
        // given
        $userId = 'aaaa45-bbbb-cccc-ddddssswwwdw';
    
        $expectedResult = [
            'userExists' => true,
            'data' => [ ... ],
        ];
    
        $user = new User();
        $user->id = $userId;
    
        $this->mockedUserRepository
            ->shouldReceive('findById')
            ->once()
            ->andReturn($user);
    
        $this->mockedSummaryRepository
            ->shouldReceive('getSummaryByUserId')
            ->once()
            ->with($user->id)
            ->andReturn([ ... ]);
    
        // when
        $result = $this->summaryService->loadLiveSummaryData($userId);
    
        // then
        $this->assertSame($expectedResult, $result);
    }
    
    /**
     * @test
     */
    public function i_expect_cache_to_be_called_when_loading_summary_data_for_specific_user()
    {
        // given
        $userId = 'aaaa45-bbbb-cccc-ddddssswwwdw';
    
        $expectedResult = [
            'userExists' => true,
            'data' => [ ... ],
        ];
    
        $summaryCacheKey = $userId . '_summary_cache';
        $summaryCacheLifespanMinutes = 10;
    
        Cache::shouldReceive('remember')
            ->once()
            ->with($summaryCacheKey, $summaryCacheLifespanMinutes, \Mockery::on(function($value) {
                return is_callable($value);
            }))
            ->andReturn($expectedResult);
    
        // when
        $result = $this->summaryService->getSummaryData($userId);
    
        // then
        $this->assertSame($expectedResult, $result);
    }
    

    让我知道是否有更好或“正确”的方法来做到这一点。

    【讨论】:

      【解决方案2】:

      有类似的情况,我想测试两个路径,当数据通过缓存返回时,以及回调函数何时执行。

      对我来说关键是不要使用任何外观模拟方法(例如Cache::shouldReceive('remember')),然后回调代码将运行。

      现在看起来很明显:(

      【讨论】:

        猜你喜欢
        • 2020-05-23
        • 2014-09-05
        • 1970-01-01
        • 2020-11-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多