带有 Mock 示例测试的 PHPSpec 始终 returns null 但实现按预期工作
PHPSpec with Mock example test always returns null but implementation works as expected
我想从 PHPSpec 开始,所以我正在使用两个简单的 类。第一个负责将百分比缩小或放大到数字,第二个负责使用百分比应用程序(如 Mock)计算产品价格。
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);
}
}
问题
问题是在上面的例子中 运行ning phpspec 运行 结果是:
- 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...
您的情况不需要模拟。存根就可以了。在 PHP Test doubles patterns with prophecy.
中阅读更多关于测试替身的信息
而不是模拟调用:
$percentageToNumberApplyer->reduce($number, $discount)->shouldBeCalled();
存根:
$percentageToNumberApplyer->reduce($number, $discount)->willReturn(80);
接下来,你只需要期望计算出的实际上是returned:
$percentageToNumberApplyer->reduce($number, $discount)->willReturn(80);
$this->applyDiscountTo($number, $discount)->shouldReturn(80);
这是因为您不应该关心是否已拨打电话。你只对结果感兴趣。
根据经验,您通常会:
- 执行操作的模拟协作者(命令方法)
- stub 合作者 return 某事(查询方法)
大多数时候最好将两者分开(Command/Query分开),我们不需要同时模拟和存根。
我想从 PHPSpec 开始,所以我正在使用两个简单的 类。第一个负责将百分比缩小或放大到数字,第二个负责使用百分比应用程序(如 Mock)计算产品价格。
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);
}
}
问题
问题是在上面的例子中 运行ning phpspec 运行 结果是:
- 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...
您的情况不需要模拟。存根就可以了。在 PHP Test doubles patterns with prophecy.
中阅读更多关于测试替身的信息而不是模拟调用:
$percentageToNumberApplyer->reduce($number, $discount)->shouldBeCalled();
存根:
$percentageToNumberApplyer->reduce($number, $discount)->willReturn(80);
接下来,你只需要期望计算出的实际上是returned:
$percentageToNumberApplyer->reduce($number, $discount)->willReturn(80);
$this->applyDiscountTo($number, $discount)->shouldReturn(80);
这是因为您不应该关心是否已拨打电话。你只对结果感兴趣。
根据经验,您通常会:
- 执行操作的模拟协作者(命令方法)
- stub 合作者 return 某事(查询方法)
大多数时候最好将两者分开(Command/Query分开),我们不需要同时模拟和存根。