如何对Laravel的Cache :: remember功能的所有代码执行路径进行单元测试?

时间:2019-04-17 10:25:51

标签: php laravel unit-testing phpunit mockery

我非常喜欢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))。该方法是意外的还是   参数与该方法的预期参数列表不匹配

     

Objects:(array('Closure'=> array(       'class'=>'关闭',       '属性'=>       数组(       ),),))

关于如何正确测试的任何想法?

2 个答案:

答案 0 :(得分:0)

好的,我似乎已经把这个复杂化了;所以我将其分解并像这样稍有不同固定。

我的服务代码现在看起来像这样:

/**
 * 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);
}

让我知道是否存在更好或“正确”的方法。

答案 1 :(得分:0)

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

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

现在看起来很明显:(