在 Laravel 中使用 PhpSpec 模拟新对象创建
Mocking new object creation with PhpSpec in Laravel
我有一个名为 Campaign
的 class,负责在外部 API 中预订单个活动。我有 EntryBooking
class 负责准备条目并使用 Campaign
class 进行预订。在某些情况下,我想创建多个活动,因此我想为每个活动创建一个新的 Campaign
对象并对其调用 book()
方法。 (每个活动都有自己的 Campaign
对象)
我遇到的问题是我想对 EntryBooking
class 进行单元测试并且我想模拟 Campaign
对象。
我正在使用 BenConstable/phpspec-laravel
包,因此我可以访问我的规范中的外观。
我正在尝试这样做:
# EntryBookingSpec.php
function it_should_book_campaigns_for_entry(Entry $entry, Campaign $campaignMock)
{
$campaignMock->book()->shouldBeCalled();
App::instance(Campaign::class, $campaignMock);
$this->bookForEntry($entry);
}
-
# EntryBooking.php
class EntryBooking
{
public function bookForEntry(Entry $entry): void
{
$campaign = App::make(Campaign::class);
// do the processing and set values for $campaign
$campaign->book();
}
}
我正在尝试使用 App::instance()
,因为在实时环境中,App::make()
每次调用时都会创建一个新实例,但在测试时我希望它 return 完全相同对象,以便我可以对其进行断言。
问题是预测失败。即使我调用 $campaignMock->book()->shouldBeCalled();
并在测试中调用 class 我调用 $campaign->book()
我仍然得到:
some predictions failed:
Double\vendor\package\Campaign\P1:
No calls have been made that match:
Double\vendor\package\Campaign\P1->book()
but expected at least one.
事实证明,当您使用构造函数进行依赖注入时,PhpSpec 会自动从您的 MethodProphecy
创建一个模拟对象,但是如果您希望您的服务容器成为 return 模拟对象,您需要传递它是你自己包装的对象。
所以改变以下
App::instance(Campaign::class, $campaignMock);
到
App::instance(Campaign::class, $campaignMock->getWrappedObject());
使一切按预期工作。
编辑
或者更好的是,完全模拟服务容器:
App::shouldReceive('make')
->with(Campaign::class)
->andReturn($campaignMock->getWrappedObject());
并在 letGo()
函数中调用 \Mockery::close()
。
class 无法接收具体的 class 实现,您实际测试 class 是否首先向服务容器发出了解析请求。
我有一个名为 Campaign
的 class,负责在外部 API 中预订单个活动。我有 EntryBooking
class 负责准备条目并使用 Campaign
class 进行预订。在某些情况下,我想创建多个活动,因此我想为每个活动创建一个新的 Campaign
对象并对其调用 book()
方法。 (每个活动都有自己的 Campaign
对象)
我遇到的问题是我想对 EntryBooking
class 进行单元测试并且我想模拟 Campaign
对象。
我正在使用 BenConstable/phpspec-laravel
包,因此我可以访问我的规范中的外观。
我正在尝试这样做:
# EntryBookingSpec.php
function it_should_book_campaigns_for_entry(Entry $entry, Campaign $campaignMock)
{
$campaignMock->book()->shouldBeCalled();
App::instance(Campaign::class, $campaignMock);
$this->bookForEntry($entry);
}
-
# EntryBooking.php
class EntryBooking
{
public function bookForEntry(Entry $entry): void
{
$campaign = App::make(Campaign::class);
// do the processing and set values for $campaign
$campaign->book();
}
}
我正在尝试使用 App::instance()
,因为在实时环境中,App::make()
每次调用时都会创建一个新实例,但在测试时我希望它 return 完全相同对象,以便我可以对其进行断言。
问题是预测失败。即使我调用 $campaignMock->book()->shouldBeCalled();
并在测试中调用 class 我调用 $campaign->book()
我仍然得到:
some predictions failed:
Double\vendor\package\Campaign\P1:
No calls have been made that match:
Double\vendor\package\Campaign\P1->book()
but expected at least one.
事实证明,当您使用构造函数进行依赖注入时,PhpSpec 会自动从您的 MethodProphecy
创建一个模拟对象,但是如果您希望您的服务容器成为 return 模拟对象,您需要传递它是你自己包装的对象。
所以改变以下
App::instance(Campaign::class, $campaignMock);
到
App::instance(Campaign::class, $campaignMock->getWrappedObject());
使一切按预期工作。
编辑
或者更好的是,完全模拟服务容器:
App::shouldReceive('make')
->with(Campaign::class)
->andReturn($campaignMock->getWrappedObject());
并在 letGo()
函数中调用 \Mockery::close()
。
class 无法接收具体的 class 实现,您实际测试 class 是否首先向服务容器发出了解析请求。