浏览器测试时,是否多次调用了Cache模拟?

时间:2018-08-23 15:49:39

标签: unit-testing laravel-5 mocking

我正在尝试介绍以下内容:

Uncovered line

Resulting in Uncovered Method

我正在使用以下测试代码:

public function test_it_deletes_a_patient()
{
    // ...

    $cacheKey = vsprintf('%s.%s', [$this->doctorUser->id, 'backoffice.stats.patientsTotalCount']);
    Cache::shouldReceive('has')->with($cacheKey)->once()->andReturn(false);
    Cache::shouldReceive('increment')->with($cacheKey, -1)->once()->andReturn(true);

    $response = $this->json('DELETE', route('patients.destroy', $this->patient), ['confirmation' => 'ELIMINAR']);

    // ...
}

这会触发以下控制器代码:

public function destroy(Patient $patient, Request $request)
{
    $this->authorize('delete', $patient);

    $confirmation = $request->get('confirmation');

    if ($confirmation != 'ELIMINAR') {
        return response()->json(['success' => false]);
    }

    logger()->info("Deleting Patient Profile PATIENT_ID:[{$patient->id}]");

    $patient->delete();

    $this->updatePatientsCount(-1);

    return response()->json(['success' => true]);
}

protected function updatePatientsCount($amount = 1)
{
    $key = vsprintf('%s.%s', [auth()->user()->id, 'backoffice.stats.patientsTotalCount']);
    if (Cache::has($key)) { // I want to mock for testing this
        Cache::increment($key, $amount); // I want to mock for testing this
    }
}

试运行后,我得到:

alariva@trinsic:~/fimedi$ t --filter=test_it_deletes_a_patient
PHPUnit 7.3.1 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 6.53 seconds, Memory: 26.00MB

There was 1 failure:

1) Tests\Browser\Backoffice\PatientsTest::test_it_deletes_a_patient
Unable to find JSON fragment
["success":true]
within
[{"exception":"Mockery\\Exception\\NoMatchingExpectationException","file":"\/home\/alariva\/fimedi\/vendor\/mockery\/mockery\/library\/Mockery\/ExpectationDirector.php","line":92,"message":"No matching handler found for Mockery_0_Illuminate_Cache_CacheManager::has('2056e535e689ab723b3f44831b488f05f7fb8b90'). Either the method was unexpected or its arguments matched no expected argument list for this method\n\n","trace":[{"class":"App\\Http\\Middleware\\Language","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Barryvdh\\Debugbar\\Middleware\\InjectDebugbar","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Illuminate\\Auth\\Middleware\\Authenticate","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Illuminate\\Cookie\\Middleware\\EncryptCookies","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Il

经过几次测试后,我的解释是,一旦我模拟Cache,它就会在到达测试块之前被某些中间件调用,因此,由于未模拟那些被调用的方法,因此测试失败因为它不知道该为那些中间件调用做什么。

想象一下,在进入测试的代码块之前,我可以成功模拟所有调用,我可以使其达到目标。但这不是解决它的方法。

如何模拟Cache并避免由于先前未测试的Cache调用而失败?

编辑:在寻求解决方案后,我意识到这是一个误导性问题。我的实际需求是:

如何成功覆盖这些行?


侧注::如果我尝试禁用中间件($this->withoutMiddleware();),则会得到AccessDeniedHttpException

alariva@trinsic:~/fimedi$ t --filter=test_it_deletes_a_patient
PHPUnit 7.3.1 by Sebastian Bergmann and contributors.

F                                                                   1 / 1 (100%)

Time: 12.95 seconds, Memory: 24.00MB

There was 1 failure:

1) Tests\Browser\Backoffice\PatientsTest::test_it_deletes_a_patient
Unable to find JSON fragment
["success":true]
within
[{"exception":"Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Exceptions\/Handler.php","line":201,"message":"This action is unauthorized.","trace":[{"class":"App\\Exceptions\\Handler","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Routing\/Pipeline.php","function":"render","line":83,"type":"->"},{"class":"Illuminate\\Foundation\\Exceptions\\Handler","file":"\/home\/alariva\/fimedi\/app\/Exceptions\/Handler.php","function":"render","line":65,"type":"->"},{"class":"Illuminate\\Foundation\\Exceptions\\Handler","file":

也许我可以选择禁用中间件吗?

1 个答案:

答案 0 :(得分:3)

我设法通过将自定义Cache操作封装到一个宏中来涵盖控制器的方法,从而获得拆分成代码单元的好处。

  1. 我将代码移到了(位于服务提供商的boot()中):

    Cache::macro('incrementExisting', function($key, $amount) {
        if (Cache::has($key)) {
            Cache::increment($key, $amount);
        }
        return $this;
    });
    
  2. 我重构为使用宏

    protected function updatePatientsCount($amount = 1)
    {
        $key = vsprintf('%s.%s', [auth()->user()->id, 'backoffice.stats.patientsTotalCount']);
        Cache::incrementExisting($key, $amount);
    }
    

我可以获得所需的覆盖率,而我仍然可以使用单元测试来测试重构的代码。

Test coverage result


更新我

关于处理许多未被嘲笑的呼叫的问题,I just learned from Adam Wathan存在shouldIgnoreMissing(),因此可以在这种情况下使用模拟方法。

更新II

Write your tests first.这样做可以避免难以测试的代码。