php - 在 PHPSpec stub 上仅模拟一种方法

标签 php testing mocking phpspec stubs

好吧,我正在尝试将我的一个包移到 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/

相关文章:

php - 将某些命名空间添加到 PHP 中的 SOAP 信封

测试软件: fake vs stub

php - 将二维数组提交到单元测试 laravel 失败

python - pytest - 修补类不起作用,而是调用类

java - 缺少依赖项的模拟类

php sql 错误消息更改了数据库上下文

php - 以 mvc 模式传递变量

php - 如何检查 guzzle 请求的有效负载

php - MySQL 查询故障排除

ruby-on-rails - 在 Ruby on Rails 中使用 get 时测试抛出异常