我想知道是否有一种简洁的方式来模拟支持方法链的对象...例如,数据库查询对象可能具有如下所示的方法调用:
$result = $database->select('my_table')->where(array('my_field'=>'a_value'))->limit(1)->execute();
问题在于,如果我必须模拟两个不同的选择查询以返回不同的结果。有什么想法吗?
这是关于PHPUnit的特定问题,但其他单元测试框架的经验也会有所帮助。
我想知道是否有一种简洁的方式来模拟支持方法链的对象...例如,数据库查询对象可能具有如下所示的方法调用:
$result = $database->select('my_table')->where(array('my_field'=>'a_value'))->limit(1)->execute();
我不确定这是否是您要找的,所以请留下评论:
class StubTest extends PHPUnit_Framework_TestCase
{
public function testChainingStub()
{
// Creating the stub with the methods to be called
$stub = $this->getMock('Zend_Db_Select', array(
'select', 'where', 'limit', 'execute'
), array(), '', FALSE);
// telling the stub to return a certain result on execute
$stub->expects($this->any())
->method('execute')
->will($this->returnValue('expected result'));
// telling the stub to return itself on any other calls
$stub->expects($this->any())
->method($this->anything())
->will($this->returnValue($stub));
// testing that we can chain the stub
$this->assertSame(
'expected result',
$stub->select('my_table')
->where(array('my_field'=>'a_value'))
->limit(1)
->execute()
);
}
}
你可以将此与期望值结合使用:
class StubTest extends PHPUnit_Framework_TestCase
{
public function testChainingStub()
{
// Creating the stub with the methods to be called
$stub = $this->getMock('Zend_Db_Select', array(
'select', 'where', 'limit', 'execute'
), array(), '', FALSE);
// overwriting stub to return something when execute is called
$stub->expects($this->exactly(1))
->method('execute')
->will($this->returnValue('expected result'));
$stub->expects($this->exactly(1))
->method('limit')
->with($this->equalTo(1))
->will($this->returnValue($stub));
$stub->expects($this->exactly(1))
->method('where')
->with($this->equalTo(array('my_field'=>'a_value')))
->will($this->returnValue($stub));
$stub->expects($this->exactly(1))
->method('select')
->with($this->equalTo('my_table'))
->will($this->returnValue($stub));
// testing that we can chain the stub
$this->assertSame(
'expected result',
$stub->select('my_table')
->where(array('my_field'=>'a_value'))
->limit(1)
->execute()
);
}
}
$this->at($index)
来从多次调用 execute()
中返回不同的值。不幸的是,这里的 $index
计算的是对同一个模拟对象(如 select()
和 where()
[所有模拟对象?我忘了])的所有方法调用,所以正确设置它们可能会非常麻烦。你最好编写一个自定义存根类。 - David Harkness我知道这是一个老问题,但它可能会对未来的谷歌搜索者有所帮助。
我也曾经遇到过在寻找一个能够提供简单易用的语法来模拟和存根方法链的框架方面的问题。然后我决定编写一个简单易用的模拟库。
使用示例:
// Creating a new mock for SimpleClassForMocking
$mock = ShortifyPunit::mock('SimpleClassForMocking');
ShortifyPunit::when($mock)->first_method()
->second_method(2,3)->returns(1);
ShortifyPunit::when($mock)->first_method()
->second_method(2,3,4)->returns(2);
ShortifyPunit::when($mock)->first_method(1)
->second_method(2,3,4)->returns(3);
ShortifyPunit::when($mock)->first_method(1,2,3)
->second_method(1,2)->third_method()->returns(4);
$mock->first_method()->second_method(2,3); // returns 1
$mock->first_method()->second_method(2,3,4); // returns 2
$mock->first_method(1)->second_method(2,3,4); // returns 3
$mock->first_method(1,2,3)->second_method(1,2)->third_method(); // return 4
https://github.com/danrevah/ShortifyPunit#stubbing-method-chanining
这可能不是你想要的答案,但我几年前写了一个模拟对象框架,可以很好地处理这种“取决于输入”的断言:
http://code.google.com/p/yaymock/
http://code.google.com/p/yaymock/wiki/Expectations
我写这个是为了在支持 Swift Mailer 的单元测试中使用,但是据我所知它并没有被其他项目广泛采用。其目的是提供比 PHPUnit 和 SimpleTest 提供的模拟对象调用更好的控制和内省。