我正在学习测试的技巧,需要您的一些帮助。我有一个功能(旧版代码)需要测试,该功能接收一些数据作为参数,将其暂时保存在数据库中并发送到外部API。如果api响应为200,则更新表中的数据以使其永久保存,否则将其删除。
这是代码:
public function sendAssignments(Challenge $challenge, int $reportType)
{
$reportModel = new Report($challenge, $reportType);
$pointsList = $this->calculateAssignments($challenge, $reportType);
$pharmsToSend = $pointsList['pharmsCollection'];
$pointsArray = $pointsList['pointsArray'];
$savedAt = Date('Y-m-d H:i:s');
$symfonyService = new SymfonyService();
$requestUid = \uniqid('AS-', false);
$sentAt = Date('Y-m-d H:i:s');
$reportModel->persistReportAssignments($pharmsToSend, $savedAt, $sentAt, false);
$symfonyResult = $symfonyService->sendReportPoints(
$requestUid,
$challenge->challenge_id,
$pointsArray
);
if ($symfonyResult->code == "200") {
// Actualizamos los datos
$reportModel->persistReportAssignments($pharmsToSend, $savedAt, $sentAt, true);
FlashMessage::addSuccess('Se han asignado los puntos del reporte.', 'Asignación correcta');
} else {
$reportModel->deleteFailedAssignmentsFromDatabase($savedAt);
FlashMessage::addError($symfonyResult->msg, 'Error '.$symfonyResult->code.' - No se han asignado los puntos');
}
if ($reportType == Report::REPORT_CONNECTED) {
return redirect()->route('reports.showConnectedReport', ['challenge'=>$challenge]);
} else if ($reportType == Report::REPORT_MANUAL) {
return redirect()->route('reports.showManualReport', ['challenge'=>$challenge]);
}
}
我正在测试功能而不是API调用,因此我想模拟SymfonyService类,为此,我编写了一个新类:
class SymfonyServiceMock
{
public function sendReportPoints()
{
return ["operation"=>"ok", "code" => 200];
}
}
但是问题在于SymfonyService正在功能中实例化,因此我不能使用SymfonyServiceMock类来模拟API调用。这是我的疑问,我是否必须重构该功能才能将SymfonyService传递给它?这是正确的方法吗?
我的意思是像这样的重构:
public function sendAssignments(Challenge $challenge, int $reportType, SymfonyService $symfonyService)
{
// The rest of the code here, but without instantiating SymfonyService
}
如果您对如何最好的测试方法有更好的想法(当然要遵循SOLID原则),我将很高兴知道。
非常感谢
编辑: 我有这个主意,我已经在AppServiceContainer上注册了SymfonyService,现在我将SymfonyService注入到控制器上,以便可以像这样重构:
public function sendAssignments(Challenge $challenge, int $reportType)
{
$this->executeSendAssignmentsOperation($challenge, $reportType);
if ($reportType == Report::REPORT_CONNECTED) {
return redirect()->route('reports.showConnectedReport', ['challenge'=>$challenge]);
} else if ($reportType == Report::REPORT_MANUAL) {
return redirect()->route('reports.showManualReport', ['challenge'=>$challenge]);
}
}
public function executeSendAssignmentsOperation(Challenge $challenge, int $reportType)
{
$reportModel = new Report($challenge, $reportType);
$pointsList = $this->calculateAssignments($challenge, $reportType);
$pharmsToSend = $pointsList['pharmsCollection'];
$pointsArray = $pointsList['pointsArray'];
$savedAt = Date('Y-m-d H:i:s');
$sentAt = Date('Y-m-d H:i:s');
$reportModel->persistReportAssignments($pharmsToSend, $savedAt, $sentAt, false);
$symfonyResult = $this->_symfonyService->sendReportPoints(
$challenge,
$pointsArray,
$reportType
);
$sentAt = Date('Y-m-d H:i:s');
if ($symfonyResult->code == "200") {
// Actualizamos los datos
$reportModel->persistReportAssignments($pharmsToSend, $savedAt, $sentAt, true);
FlashMessage::addSuccess('Se han asignado los puntos del reporte.', 'Asignación correcta');
} else {
$reportModel->deleteFailedAssignmentsFromDatabase($savedAt);
FlashMessage::addError($symfonyResult->msg, 'Error '.$symfonyResult->code.' - No se han asignado los puntos');
}
}
现在有了这个重构,我可以编写测试了。但是,我仍然想知道这是否是解决问题的正确方法。如果没有,您能解释一下原因吗?
答案 0 :(得分:0)
PhpUnit也有同样的问题。不能模拟在“被测系统”中创建的类。
对我来说唯一有效的解决方案是使用Kahlan(https://github.com/kahlan/kahlan)测试代码,该代码可以动态模拟类。但这需要通过composer自动加载类。
也许这会有所帮助:)