0

我想从 PHPSpec 开始,所以我正在使用两个简单的类。第一个负责应用百分比缩小或放大到数字,第二个负责使用百分比应用程序(如模拟)计算产品价格。

PercentageToNumberApplyerSpec(规范):

namespace spec\My;

use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class PercentageToNumberApplyerSpec extends ObjectBehavior
{
    function it_is_initializable()
    {
        $this->shouldHaveType('My\PercentageToNumberApplyer');
    }

    function it_enlarges_a_number_with_a_given_percentage()
    {
        $this->enlarge(100, 20)->shouldReturn(120);
        $this->enlarge(80, 25)->shouldReturn(100);
        $this->enlarge(20, 50)->shouldReturn(30);
    }

    function it_reduces_a_number_with_a_given_percentage()
    {
        $this->reduce(100, 20)->shouldReturn(80);
        $this->reduce(80, 10)->shouldReturn(72);
        $this->reduce(250, 20)->shouldReturn(200);
    }
}

PercentageToNumberApplyer(实现):

<?php

namespace My;

class PercentageToNumberApplyer
{
    /**
     * Enlarge given number with a given percentage
     *
     * @param $number
     * @param $percentage
     * @return float
     */
    public function enlarge($number, $percentage)
    {
        return $this->calculate($number, $percentage) + $number;
    }

    /**
     * Reduce given number with a given percentage
     *
     * @param $number
     * @param $percentage
     * @return mixed
     */
    public function reduce($number, $percentage)
    {
        return $number - $this->calculate($number, $percentage);
    }

    /**
     * @param $number
     * @param $percentage
     * @return float
     */
    private function calculate($number, $percentage)
    {
        return $number * $percentage / 100;
    }
}

PriceCalculatorSpec(规格):

<?php

namespace spec\My;

use My\PercentageToNumberApplyer;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class PriceCalculatorSpec extends ObjectBehavior
{
    function let(PercentageToNumberApplyer $percentageToNumberApplyer)
    {
        $this->beConstructedWith($percentageToNumberApplyer);
    }

    function it_calculates_price_discount($percentageToNumberApplyer)
    {
        $number = 100;
        $discount = 20;

        $percentageToNumberApplyer->reduce($number, $discount)->shouldBeCalled();

        $this->applyDiscountTo($number, $discount)->shouldReturn(80);
    }
}

问题

问题是在上面的例子中运行 phpspec run 后的结果是:

- it calculates price discount
expected [integer:80], but got null

价格计算器(实现):

<?php

namespace My;

class PriceCalculator
{
    /**
     * @var PercentageToNumberApplyer
     */
    private $percentageToNumberApplyer;

    /**
     * @param PercentageToNumberApplyer $percentageToNumberApplyer
     */
    public function __construct(PercentageToNumberApplyer $percentageToNumberApplyer)
    {
        $this->percentageToNumberApplyer = $percentageToNumberApplyer;
    }

    /**
     * @param $basePrice
     * @param $discount
     * @return mixed
     */
    public function applyDiscountTo($basePrice, $discount)
    {
        return $this->percentageToNumberApplyer->reduce($basePrice, $discount);
    }
}

即使测试失败,为什么以下用例仍然有效:

$priceCalculator = new \My\PriceCalculator(new \My\PercentageToNumberApplyer());

$price = $priceCalculator->applyDiscountTo(100, 20);

$price 有 80 的价值...

4

1 回答 1

1

您的情况不需要模拟。存根会做。阅读PHP Test doubles patterns with prophecy中有关测试替身的更多信息。

而不是模拟呼叫:

$percentageToNumberApplyer->reduce($number, $discount)->shouldBeCalled();

存根:

$percentageToNumberApplyer->reduce($number, $discount)->willReturn(80);

接下来,您只需要期望实际返回的是计算的内容:

$percentageToNumberApplyer->reduce($number, $discount)->willReturn(80);

$this->applyDiscountTo($number, $discount)->shouldReturn(80);

这是因为您不应该关心是否拨打了电话。你只对结果感兴趣。

根据经验,您通常会:

  • 模拟执行操作的协作者(命令方法)
  • 返回某些东西的存根协作者(查询方法)

大多数时候最好将两者分开(命令/查询分离),我们不需要同时模拟和存根。

于 2015-03-18T15:32:54.293 回答