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)
也许我可以挑选中间件来禁用?
我通过将自定义的 Cache 操作封装到宏中来覆盖控制器的方法,从而获得拆分为代码单元的好处。
我将代码移至宏中(在boot()服务提供商的宏中):
Cache::macro('incrementExisting', function($key, $amount) {
if (Cache::has($key)) {
Cache::increment($key, $amount);
}
return $this;
});
Run Code Online (Sandbox Code Playgroud)我重构为使用宏
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()并且允许在这种情况下使用模拟方法。
首先编写你的测试。这样做时,可以更轻松地避免难以测试的代码。