如何使用 Laravel、PHPUnit、Prophecy、DI 进行模拟或存根
How to mock or stub with Laravel, PHPUnit, Prophecy, DI
你好,我正在尝试使用 Prophecy 进行我的第一个模拟或存根测试。我从未使用过 mocks 和 stubs 或 Mockery。我做了很多单元测试,其中依赖关系并没有真正发挥作用,等等。
如果我能让这个测试正常工作,我想它会帮助我进一步测试我的代码。任何帮助将不胜感激!
首先是错误...
Method `Double\App\Services\Maintenance\Flags\MaintenanceFlagsProvider\P1::findMostRecentByLabel()` is not defined.
/Library/WebServer/App/tests/unit/MaintenanceStatusTests.php:21
这里是测试的相关部分class ...
use App\Services\Maintenance\Logs\MaintenanceLogProvider;
use App\Services\Maintenance\Flags\MaintenanceFlagsProvider;
use App\Services\Maintenance\NextDueSchedules\NextDueScheduleProvider;
use App\Services\Components\Requirements\Status\RequirementStatusProvider;
use App\Services\Components\Requirements\Properties\ComponentRequirementPropertiesProvider;
class MaintenanceStatusTests extends TestCase
{
/** @test */
public function is_initial_returns_true()
{
$status = $this->buildStatus();
$status->maintenance_flags_provider->shouldReceive('findMostRecentByLabel')->andReturn(new MaintenanceFlag(['value' => 1]));
$this->assertTrue($status->isInitial());
}
private function buildStatus()
{
$maintenance_flags_provider = $this->prophesize(MaintenanceFlagsProvider::class);
$maintenance_log_provider = $this->prophesize(MaintenanceLogProvider::class);
$next_due_schedule_provider = $this->prophesize(NextDueScheduleProvider::class);
$component_requirement_properties_provider = $this->prophesize(ComponentRequirementPropertiesProvider::class);
return new RequirementStatusProvider($maintenance_flags_provider->reveal(), $maintenance_log_provider->reveal(), $next_due_schedule_provider->reveal(), $component_requirement_properties_provider->reveal());
}
}
这里是 class/method 我正在尝试测试的相关部分...
namespace App\Services\Components\Requirements\Status;
use Carbon, StdClass;
use App\Services\Maintenance\Logs\MaintenanceLogProvider;
use App\Services\Maintenance\Flags\MaintenanceFlagsProvider;
use App\Services\Maintenance\NextDueSchedules\NextDueScheduleProvider;
use App\Services\Components\Requirements\Properties\ComponentRequirementPropertiesProvider;
class RequirementStatusProvider
{
public $maintenance_flags_provider;
public $maintenance_log_provider;
public $next_due_schedule_provider;
public $component_requirement_properties_provider;
public $data;
/**
* @param MaintenanceFlagsProvider $maintenance_flags_provider
* @param MaintenanceLogProvider $maintenance_log_provider
* @param NextDueScheduleProvider $next_due_schedule_provider
* @param ComponentRequirementPropertiesProvider $component_requirement_properties_provider
*/
public function __construct(MaintenanceFlagsProvider $maintenance_flags_provider, MaintenanceLogProvider $maintenance_log_provider, NextDueScheduleProvider $next_due_schedule_provider, ComponentRequirementPropertiesProvider $component_requirement_properties_provider)
{
$this->data = new StdClass();
$this->maintenance_flags_provider = $maintenance_flags_provider;
$this->maintenance_log_provider = $maintenance_log_provider;
$this->next_due_schedule_provider = $next_due_schedule_provider;
$this->component_requirement_properties_provider = $component_requirement_properties_provider;
}
// THIS IS WHAT I'M TRYING TO TEST ....
public function isInitial()
{
$flag = $this->maintenance_flags_provider->findMostRecentByLabel('initial', $this->component->id, $this->requirement->id, $this->data->datetime);
return (($flag && 1 == $flag->getAttribute('value')) || ! $this->data->pcw_logs[0]->event_at) ? 1 : 0;
}
}
我也按照以下方式修改了测试...
/** @test */
public function is_initial_returns_true()
{
$maintenance_log_provider = $this->prophesize(MaintenanceLogProvider::class);
$maintenance_flags_provider = $this->prophesize(MaintenanceFlagsProvider::class);
$next_due_schedule_provider = $this->prophesize(NextDueScheduleProvider::class);
$component_requirement_properties_provider = $this->prophesize(ComponentRequirementPropertiesProvider::class);
$maintenance_flags_provider->findMostRecentByLabel('initial', 1, 1, Carbon::now())->willReturn(new MaintenanceFlag(['value' => 1]));
$status = new RequirementStatusProvider($maintenance_flags_provider->reveal(), $maintenance_log_provider->reveal(), $next_due_schedule_provider->reveal(), $component_requirement_properties_provider->reveal());
$this->assertTrue($status->isInitial());
}
即使我省略了 ->willReturn 部分,我仍然得到完全相同的错误。
我不希望 isInitial 调用实际的 MaintenanceFlagsProvider class,我希望测试伪造该调用,但通过测试将 $flag 变量设置为我想要的任何值(如果这有意义的话)。
部分问题出在我的提供者身上,它将未定义的方法调用转发到相应的存储库 class。通过为提供程序缺少的方法添加@method 签名,我能够让 Prophecy 开心 class
/**
* @method findMostRecentByLabel(array $args = [])
*/
class MaintenanceFlagsProvider
{
...
}
工作测试...
/** @test */
public function is_initial_returns_true()
{
$date = Carbon::now();
$maintenance_log_provider = $this->prophesize(MaintenanceLogProvider::class);
$maintenance_flags_provider = $this->prophesize(MaintenanceFlagsProvider::class);
$next_due_schedule_provider = $this->prophesize(NextDueScheduleProvider::class);
$component_requirement_properties_provider = $this->prophesize(ComponentRequirementPropertiesProvider::class);
$maintenance_flags_provider->findMostRecentByLabel('initial', 1, 1, $date)->shouldBeCalled()->willReturn(new MaintenanceFlag(['value' => 1]));
$status = new RequirementStatusProvider($maintenance_flags_provider->reveal(), $maintenance_log_provider->reveal(), $next_due_schedule_provider->reveal(), $component_requirement_properties_provider->reveal());
$status->component = (new Component())->forceFill(['id' => 1]);
$status->requirement = (new MaintenanceRequirement())->forceFill(['id' => 1]);
$status->data->datetime = $date;
$this->assertTrue($status->isInitial());
}
你好,我正在尝试使用 Prophecy 进行我的第一个模拟或存根测试。我从未使用过 mocks 和 stubs 或 Mockery。我做了很多单元测试,其中依赖关系并没有真正发挥作用,等等。
如果我能让这个测试正常工作,我想它会帮助我进一步测试我的代码。任何帮助将不胜感激!
首先是错误...
Method `Double\App\Services\Maintenance\Flags\MaintenanceFlagsProvider\P1::findMostRecentByLabel()` is not defined.
/Library/WebServer/App/tests/unit/MaintenanceStatusTests.php:21
这里是测试的相关部分class ...
use App\Services\Maintenance\Logs\MaintenanceLogProvider;
use App\Services\Maintenance\Flags\MaintenanceFlagsProvider;
use App\Services\Maintenance\NextDueSchedules\NextDueScheduleProvider;
use App\Services\Components\Requirements\Status\RequirementStatusProvider;
use App\Services\Components\Requirements\Properties\ComponentRequirementPropertiesProvider;
class MaintenanceStatusTests extends TestCase
{
/** @test */
public function is_initial_returns_true()
{
$status = $this->buildStatus();
$status->maintenance_flags_provider->shouldReceive('findMostRecentByLabel')->andReturn(new MaintenanceFlag(['value' => 1]));
$this->assertTrue($status->isInitial());
}
private function buildStatus()
{
$maintenance_flags_provider = $this->prophesize(MaintenanceFlagsProvider::class);
$maintenance_log_provider = $this->prophesize(MaintenanceLogProvider::class);
$next_due_schedule_provider = $this->prophesize(NextDueScheduleProvider::class);
$component_requirement_properties_provider = $this->prophesize(ComponentRequirementPropertiesProvider::class);
return new RequirementStatusProvider($maintenance_flags_provider->reveal(), $maintenance_log_provider->reveal(), $next_due_schedule_provider->reveal(), $component_requirement_properties_provider->reveal());
}
}
这里是 class/method 我正在尝试测试的相关部分...
namespace App\Services\Components\Requirements\Status;
use Carbon, StdClass;
use App\Services\Maintenance\Logs\MaintenanceLogProvider;
use App\Services\Maintenance\Flags\MaintenanceFlagsProvider;
use App\Services\Maintenance\NextDueSchedules\NextDueScheduleProvider;
use App\Services\Components\Requirements\Properties\ComponentRequirementPropertiesProvider;
class RequirementStatusProvider
{
public $maintenance_flags_provider;
public $maintenance_log_provider;
public $next_due_schedule_provider;
public $component_requirement_properties_provider;
public $data;
/**
* @param MaintenanceFlagsProvider $maintenance_flags_provider
* @param MaintenanceLogProvider $maintenance_log_provider
* @param NextDueScheduleProvider $next_due_schedule_provider
* @param ComponentRequirementPropertiesProvider $component_requirement_properties_provider
*/
public function __construct(MaintenanceFlagsProvider $maintenance_flags_provider, MaintenanceLogProvider $maintenance_log_provider, NextDueScheduleProvider $next_due_schedule_provider, ComponentRequirementPropertiesProvider $component_requirement_properties_provider)
{
$this->data = new StdClass();
$this->maintenance_flags_provider = $maintenance_flags_provider;
$this->maintenance_log_provider = $maintenance_log_provider;
$this->next_due_schedule_provider = $next_due_schedule_provider;
$this->component_requirement_properties_provider = $component_requirement_properties_provider;
}
// THIS IS WHAT I'M TRYING TO TEST ....
public function isInitial()
{
$flag = $this->maintenance_flags_provider->findMostRecentByLabel('initial', $this->component->id, $this->requirement->id, $this->data->datetime);
return (($flag && 1 == $flag->getAttribute('value')) || ! $this->data->pcw_logs[0]->event_at) ? 1 : 0;
}
}
我也按照以下方式修改了测试...
/** @test */
public function is_initial_returns_true()
{
$maintenance_log_provider = $this->prophesize(MaintenanceLogProvider::class);
$maintenance_flags_provider = $this->prophesize(MaintenanceFlagsProvider::class);
$next_due_schedule_provider = $this->prophesize(NextDueScheduleProvider::class);
$component_requirement_properties_provider = $this->prophesize(ComponentRequirementPropertiesProvider::class);
$maintenance_flags_provider->findMostRecentByLabel('initial', 1, 1, Carbon::now())->willReturn(new MaintenanceFlag(['value' => 1]));
$status = new RequirementStatusProvider($maintenance_flags_provider->reveal(), $maintenance_log_provider->reveal(), $next_due_schedule_provider->reveal(), $component_requirement_properties_provider->reveal());
$this->assertTrue($status->isInitial());
}
即使我省略了 ->willReturn 部分,我仍然得到完全相同的错误。
我不希望 isInitial 调用实际的 MaintenanceFlagsProvider class,我希望测试伪造该调用,但通过测试将 $flag 变量设置为我想要的任何值(如果这有意义的话)。
部分问题出在我的提供者身上,它将未定义的方法调用转发到相应的存储库 class。通过为提供程序缺少的方法添加@method 签名,我能够让 Prophecy 开心 class
/**
* @method findMostRecentByLabel(array $args = [])
*/
class MaintenanceFlagsProvider
{
...
}
工作测试...
/** @test */
public function is_initial_returns_true()
{
$date = Carbon::now();
$maintenance_log_provider = $this->prophesize(MaintenanceLogProvider::class);
$maintenance_flags_provider = $this->prophesize(MaintenanceFlagsProvider::class);
$next_due_schedule_provider = $this->prophesize(NextDueScheduleProvider::class);
$component_requirement_properties_provider = $this->prophesize(ComponentRequirementPropertiesProvider::class);
$maintenance_flags_provider->findMostRecentByLabel('initial', 1, 1, $date)->shouldBeCalled()->willReturn(new MaintenanceFlag(['value' => 1]));
$status = new RequirementStatusProvider($maintenance_flags_provider->reveal(), $maintenance_log_provider->reveal(), $next_due_schedule_provider->reveal(), $component_requirement_properties_provider->reveal());
$status->component = (new Component())->forceFill(['id' => 1]);
$status->requirement = (new MaintenanceRequirement())->forceFill(['id' => 1]);
$status->data->datetime = $date;
$this->assertTrue($status->isInitial());
}