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

ala*_*iva 8 unit-testing mocking laravel-5

我试图涵盖以下内容:

裸露的线条

导致未覆盖的方法

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

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']);

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

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
    }
}
Run Code Online (Sandbox Code Playgroud)

试运行后,我得到:

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
Run Code Online (Sandbox Code Playgroud)

我经过几次测试后解释的是,看起来我曾经嘲笑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":
Run Code Online (Sandbox Code Playgroud)

也许我可以挑选中间件来禁用?

ala*_*iva 3

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

  1. 我将代码移至中(在boot()服务提供商的宏中):

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

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

我可以获得所需的覆盖范围,同时仍然可以通过单元测试来测试重构的代码。

测试覆盖率结果


更新一

关于处理许多未被模拟的调用的问题,我刚刚从 Adam Wathan 那里了解到 存在shouldIgnoreMissing()并且允许在这种情况下使用模拟方法。

更新二

首先编写你的测试。这样做时,可以更轻松地避免难以测试的代码。