好吧,我正在尝试将我的一个包移到 PHPSpec 测试中,但很快我就遇到了这个问题。 packages 是一个购物车包,所以我想测试一下,当您将两个项目添加到购物车时,购物车的计数是两个,很简单。 但是,当然,在购物车中,当添加两个相同的商品时,购物车中不会有新条目,但原始商品的“数量”将为 2。所以但不是当它们是,例如,不同的尺寸。 因此,每个项目都根据其 ID 和选项由唯一的 rowId 标识。
这是生成 rowId 的代码(由 add()
方法使用):
protected function generateRowId(CartItem $item)
{
return md5($item->getId() . serialize($item->getOptions()));
}
现在我的测试是这样写的:
public function it_can_add_multiple_instances_of_a_cart_item(CartItem $cartItem1, CartItem $cartItem2)
{
$this->add($cartItem1);
$this->add($cartItem2);
$this->shouldHaveCount(2);
}
但问题是,对于 getId()
方法,两个 stub 都返回 null
。所以我尝试为该方法设置 willReturn()
,所以我的测试变成了这样:
public function it_can_add_multiple_instances_of_a_cart_item(CartItem $cartItem1, CartItem $cartItem2)
{
$cartItem1->getId()->willReturn(1);
$cartItem2->getId()->willReturn(2);
$this->add($cartItem1);
$this->add($cartItem2);
$this->shouldHaveCount(2);
}
但现在我得到错误,告诉我调用了意想不到的方法,如 getName()
。所以我必须对调用的 CartItem 接口(interface)上的所有方法执行相同的操作:
public function it_can_add_multiple_instances_of_a_cart_item(CartItem $cartItem1, CartItem $cartItem2)
{
$cartItem1->getId()->willReturn(1);
$cartItem1->getName()->willReturn(null);
$cartItem1->getPrice()->willReturn(null);
$cartItem1->getOptions()->willReturn([]);
$cartItem2->getId()->willReturn(2);
$cartItem2->getName()->willReturn(null);
$cartItem2->getPrice()->willReturn(null);
$cartItem2->getOptions()->willReturn([]);
$this->add($cartItem1);
$this->add($cartItem2);
$this->shouldHaveCount(2);
}
现在这个工作,测试是绿色的。但感觉不对……我是不是遗漏了什么或者这是对 PHPSpec 的限制?
最佳答案
Now this works, test is green. But it feels wrong... Am I missing something or is this a limitation on PHPSpec?
我认为在那种情况下感觉错了很好,因为它应该。正如 @l3l0 上面提到的,PHPSpec 是一种设计工具,它在这里为您提供有关您的设计的明确信息。
您遇到的问题是您的 Cart
违反了单一职责原则 - 它做了不止一件事 - 它管理 CartItems
并知道如何生成 RowId
来自它。因为 PHPSpec 强制您对 CartItem
的整个行为进行 stub ,所以它会向您提供一条消息以重构生成 RowId
。
现在假设您将 RowIdGenerator 提取到单独的类(这里没有介绍它自己的规范):
class RowIdGenerator
{
public function fromCartItem(CartItem $item)
{
return md5($item->getId() . serialize($item->getOptions()));
}
}
然后您通过构造函数将此生成器作为您的购物车的依赖项注入(inject):
class Cart
{
private $rowIdGenerator;
public function __construct(RowIdGenerator $rowIdGenerator)
{
$this->rowIdGenerator = $rowIdGenerator;
}
}
那么您的最终规范可能如下所示:
function let(RowIdGenerator $rowIdGenerator)
{
$this->beConstructedWith($rowIdGenerator);
}
public function it_can_add_multiple_instances_of_a_cart_item(RowIdGenerator $rowIdGenerator, CartItem $cartItem1, CartItem $cartItem2)
{
$rowIdGenerator->fromCartItem($cartItem1)->willReturn('abc');
$rowIdGenerator->fromCartItem($cartItem1)->willReturn('def');
$this->add($cartItem1);
$this->add($cartItem2);
$this->shouldHaveCount(2);
}
并且由于您模拟了 id 生成器的行为(并且您知道必须进行此通信),现在您符合 SRP。你现在感觉好点了吗?
关于php - 在 PHPSpec stub 上仅模拟一种方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27282165/