我试图模拟一个链(嵌套)方法来返回所需的值,这就是代码:
public function __construct($db)
{
$this->db = $db;
}
public function getResults()
{
return $this->db->getFinder()->find($this->DBTable);
}
我试过这个模拟但它不起作用:
$dbMock = $this->createMock(DB::class);
$dbMock = $dbMock
->expects(self::any())
->method('getFinder')
->method('find')
->with('questions')
->will($this->returnValue('7'));
任何解决方案如何解决这样的问题?
谢谢。
答案 0 :(得分:2)
虽然@BVengerov他的回答肯定会有效,但我建议改变设计。我认为链接模拟不是要走的路,它会损害可读性,更重要的是,简化测试。
我建议你让Finder
班成为班上的一员。因此,您现在只需要模拟Finder
。
class MyClass {
private $finder;
public function __construct(Finder $finder) {
$this->finder = $finder;
}
public function getResults() {
return $this->finder->find($this->DBTable);
}
}
此更改使得对此函数进行单元测试(和类!)简单。
“但我需要班级其他地方的$db
变量!”嗯,首先,这可能表明当前类中的一个类正在死亡被提取。保持课堂小而简单。
但是,作为一个快速而肮脏的解决方案,请考虑添加setFinder()
setter,以供测试使用。
答案 1 :(得分:1)
链由一个接一个地调用的对象组成。因此,您需要实现一系列模拟。只需以这样的方式模拟方法,即返回模拟对象。
这样的事情应该有效:
$finderMock = $this->createMock(Finder::class);
$finderMock = $finderMock
->expects(self::any)
->method('find')
->with('questions')
->will($this->returnValue('7'));
$dbMock = $this->createMock(DB::class);
$dbMock = $dbMock
->expects(self::any())
->method('getFinder')
->will($this->returnValue($finderMock));
详细了解模拟链in this cool blog post。
但是,我并没有真正看到测试链中的重点。 IMO最好将测试限制为一次测试1个模块(功能)或2个模块(交互)。答案 2 :(得分:1)
使用Mocking Demeter Chains And Fluent Interfaces
现在更简单简单地
$dbMock = $dbMock
->expects(self::any())
->method('getFinder->find')
->with('questions')
->will($this->returnValue('7'));
嘲讽文档中的另一个示例
$object->foo()->bar()->zebra()->alpha()->selfDestruct();
,您想让selfDestruct
返回10
$mock = \Mockery::mock('CaptainsConsole');
$mock->shouldReceive('foo->bar->zebra->alpha->selfDestruct')->andReturn(10);