好的,所以我试图将我的一个软件包移到PHPSpec测试中,但是很快我遇到了这个问题。
这些包装是购物车包装,因此我想测试一下,当您将两个商品添加到购物车时,购物车的数量为两个,很简单。
但是,当然,在购物车中,当添加两个相同的商品时,购物车中不会有新条目,但是原始商品的“数量”为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);
}


但是问题是,两个存根都为null方法返回getId()。因此,我尝试为该方法设置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接口上的所有方法进行相同的调用:

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的限制吗?

最佳答案

现在工作正常,测试为绿色。但这感觉不对...我是否缺少某些东西,或者这是对PHPSpec的限制吗?


我认为在那种情况下感觉不错是件好事,因为应该这样做。就像上面提到的@ l3l0一样,PHPSpec是一个设计工具,在这里可以为您提供有关设计的清晰信息。

您遇到的困难是您的Cart违反了单一责任原则-它做的不只是一件事-它管理CartItems并知道如何从中生成RowId。因为PHPSpec迫使您保留CartItem的整个行为,所以它会向您提供一条消息,以重构生成RowId的过程。

现在,假设您将RowIdGenerator提取为单独的类(此处未涵盖其自身的规范):

class RowIdGenerator
{
    public function fromCartItem(CartItem $item)
    {
        return md5($item->getId() . serialize($item->getOptions()));
    }
}


然后,您通过构造函数将此生成器作为对Cart的依赖项注入:

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。现在感觉好点了吗?

09-30 21:25