在PHPSpec存根上只模拟一种方法

Cri*_*ane 1 php testing mocking phpspec stubs

好的,所以我试图将我的一个软件包移到PHPSpec测试中,但很快我遇到了这个问题.这些包是一个购物车包,所以我想测试一下,当你向购物车添加两个商品时,购物车的数量为2,简单.不过,当然,在一个购物车,增加了两个相同的项目的时候,不会有在车一个新的条目,但原来的项目将得到的2"数量"所以,而不是当他们是,例如,不同的尺寸.因此,每个项目都由唯一的rowId标识,基于它的ID和选项.

这是生成rowId的代码(由add()方法使用):

protected function generateRowId(CartItem $item)
{
    return md5($item->getId() . serialize($item->getOptions()));
}
Run Code Online (Sandbox Code Playgroud)

现在我写了这样的测试:

public function it_can_add_multiple_instances_of_a_cart_item(CartItem $cartItem1, CartItem $cartItem2)
{
    $this->add($cartItem1);
    $this->add($cartItem2);

    $this->shouldHaveCount(2);
}
Run Code Online (Sandbox Code Playgroud)

但问题是,两个存根都返回nullgetId()方法.所以我尝试设置willReturn()for该方法,所以我的测试成了这样:

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);
}
Run Code Online (Sandbox Code Playgroud)

但现在我得到错误,告诉我意外的方法被称为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);
}
Run Code Online (Sandbox Code Playgroud)

现在这个工作,测试是绿色的.但感觉不对......我错过了什么或者这是对PHPSpec的限制吗?

小智 9

现在这个工作,测试是绿色的.但感觉不对......我错过了什么或者这是对PHPSpec的限制吗?

我认为在这种情况下感觉不错是因为它应该.正如上面提到的@ l3l0,PHPSpec是一个设计工具,它为您提供了有关您的设计的明确信息.

你挣扎的事实是你Cart违反了单一责任原则 - 它不止一件事 - 它管理CartItems并知道如何从中生成RowId.因为PHPSpec强制你存在CartItem它的整个行为,它会给你一个消息来重构生成RowId.

现在假设你将RowIdGenerator提取为单独的类(这里没有涉及它自己的规范):

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

然后通过构造函数将此生成器作为依赖项注入到Cart中:

class Cart
{
    private $rowIdGenerator;

    public function __construct(RowIdGenerator $rowIdGenerator)
    {
        $this->rowIdGenerator = $rowIdGenerator;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你的最终规格可能如下:

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);
}
Run Code Online (Sandbox Code Playgroud)

而且因为你嘲笑了id生成器的行为(并且你知道这种通信必须发生),所以现在你遵守了SRP.你现在觉得好些吗?