大家好我有一个方法给出一个确定的情况,它称之为自我,该方法的一个简短例子可以是:
class MyClass
{
protected $quantity;
public function add($quantity)
{
for($i = 0; $i < $quantity; $i++)
{
$newQuantity = $quantity - 1;
$this->setQuantity($newQuantity);
$this->add($this->quantity);
}
return $this->quantity;
}
public function setQuantity($quantity)
{
$this->quantity = $quantity;
}
}
如果我想为这个丑陋的方法编写一个测试(仅仅是为了举例),我会这样做:
<?php
use Mockery as m;
class TestMyClass
{
public function teardown()
{
m::close();
}
public function test_add_method()
{
// Here come the problem because I need to mock that the method
// will be called, but if I mock it, I cannot call it for an
// assertion
$mockMyClass = m::mock('MyClass[setQuantity,add]');
$mockClass->shouldReceive('setQuantity')
->once()
->with(1)
->andReturn(null);
$result = $mockMyClass->add(1); // Here the problem
$this->assertEquals(0,$result);
}
}
但是我如何编写代码上面的注释,我无法正确地模拟方法add
因为我需要对它做一个断言,但是甚至是真的会再次调用我应该完成的行为它。
错误跟踪运行此单元测试:
方法Mockery_1_Mocks_My_Class :: add()在此模拟上不存在 对象
你如何实现这个小功能的测试?
答案 0 :(得分:3)
基本的经验法则:你不要嘲笑你要测试的课程,期间。
可能存在这样的规则似乎不适用的情况,但是所有这些情况都表明你的班级做了太多事情,需要分成几部分,然后你所测试的所有部分都可以被嘲笑。 / p>
现在你似乎试图模仿一些东西,因为你想测试内部递归调用。唐&#39;吨。因为它与外界无关,如何实现结果,无论是递归还是迭代。您总是可以将递归代码转换为交互式代码,反之亦然,唯一的区别是一种方法通常更适合该问题。
所以从本质上说你想做这样的事情:
public function test_add_one()
{
$myClass = new MyClass();
$result = $myClass->add(1);
$this->assertEquals(0,$result);
}
public function test_add_two()
{
$myClass = new MyClass();
$result = $myClass->add(2);
$this->assertEquals(0,$result); // I totally made this one up, haven't checked the code in detail
}
如果测试函数看起来像这样,并且您没有看到任何有益测试,无论您传递给add方法的参数是什么,返回值始终为零,这可能与您的示例代码无关,而不显示任何其他位置的值可以观察到代码执行触发的更改。
答案 1 :(得分:1)
你的班级应该有getQuantity()
。只需运行add()
,然后对getQuantity()
进行断言。
无论如何......你可以模拟setQuantity()
(例如)抛出异常。
然后断言add
将抛出异常。
也许您可以使用add(2)
进行测试,仅针对特定参数模拟add
:)