我正在学习测试的技巧,需要您的一些帮助。我有一个功能(旧版代码)需要测试,该功能接收一些数据作为参数,将其暂时保存在数据库中并发送到外部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');
}
}
现在有了这个重构,我可以编写测试了。但是,我仍然想知道这是否是解决问题的正确方法。如果没有,请您解释一下原因?
PhpUnit也有同样的问题。无法模拟在被测系统中创建的类。
唯一对我有用的解决方案是使用Kahlan(https://github.com/kahlan/kahlan)测试代码,该代码可以动态模拟类。但是它要求通过composer将类自动加载为类。
也许这会有所帮助:)