PHPUnit-如何模拟PDO准备的语句
我正在尝试使用PHPUnit对映射器类进行单元测试。 我可以轻松地模拟将被注入mapper类的PDO实例,但我不知道如何模拟PreparedStatement类,因为它是由PDO类生成的 在我的例子中,我扩展了PDO类,因此我有以下内容:PHPUnit-如何模拟PDO准备的语句,php,mocking,pdo,phpunit,prepared-statement,Php,Mocking,Pdo,Phpunit,Prepared Statement,我正在尝试使用PHPUnit对映射器类进行单元测试。 我可以轻松地模拟将被注入mapper类的PDO实例,但我不知道如何模拟PreparedStatement类,因为它是由PDO类生成的 在我的例子中,我扩展了PDO类,因此我有以下内容: public function __construct($dsn, $user, $pass, $driverOptions) { //... parent::__construct($dsn, $user, $pass, $driverO
public function __construct($dsn, $user, $pass, $driverOptions)
{
//...
parent::__construct($dsn, $user, $pass, $driverOptions);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS,
array('Core_Db_Driver_PDOStatement', array($this)));
}
关键是核心_Db_驱动程序_PDO语句没有注入到PDO类的构造函数中,而是静态实例化的。即使我这样做:
public function __construct($dsn, $user, $pass, $driverOptions, $stmtClass = 'Core_Db_Driver_PDOStatement')
{
//...
parent::__construct($dsn, $user, $pass, $driverOptions);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS,
array($stmtClass, array($this)));
}
。。。它仍然是一个静态实例,因为我无法传递我自己的prepared statement类的模拟实例
有什么想法吗
编辑:
根据anwser改编的解决方案:
/**
* @codeCoverageIgnore
*/
private function getDbStub($result)
{
$STMTstub = $this->getMock('PDOStatement');
$STMTstub->expects($this->any())
->method('fetchAll')
->will($this->returnValue($result));
$PDOstub = $this->getMock('mockPDO');
$PDOstub->expects($this->any())
->method('prepare')
->will($this->returnValue($STMTstub));
return $PDOstub;
}
public function testGetFooById()
{
$arrResult = array( ... );
$PDOstub = $this->getDbStub($arrResult);
}
如果可以模拟PDO类,只需模拟PDO类及其所有依赖项即可。应该不需要关心语句类或pdo类的构造函数,因为您通过mock定义了输入和输出 因此,您需要一个返回模拟对象的模拟对象 这看起来可能有点令人困惑,但由于您应该只测试正在测试的类所做的事情,而不测试其他任何事情,因此您几乎可以忽略数据库连接的所有其他部分 在本例中,您需要了解的是:
- 你叫什么名字
- 在准备返回时调用fetchAll吗
- 那个呼叫的结果是否返回
<?php
class myClass {
public function __construct(ThePDOObject $pdo) {
$this->db = $pdo;
}
public function doStuff() {
$x = $this->db->prepare("...");
return $x->fetchAll();
}
}
class myClassTest extends PHPUnit_Framework_TestCase {
public function testDoStuff() {
$fetchAllMock = $this
->getMockBuilder("stdClass" /* or whatever has a fetchAll */)
->setMethods(array("fetchAll"))
->getMock();
$fetchAllMock
->expects($this->once())->method("fetchAll")
->will($this->returnValue("hello!"));
$mock = $this
->getMockBuilder("ThePDOObject")
->disableOriginalConstructor()
->setMethods(array("prepare"))
->getMock();
$mock
->expects($this->once())
->method("prepare")
->with("...")
->will($this->returnValue($fetchAllMock));
$x = new myClass($mock);
$this->assertSame("hello!", $x->doStuff());
}
}
确切地说,您确实发现了我不太理解的部分:当您可以伪造fetchAll将返回您可以在测试中确定的内容时,无需插入语句。我已经编辑了我的问题,以放置我的修改过的解决方案。谢谢这种方法的问题在于它将应用程序代码实现公开给测试。测试是否应该知道应用程序是调用->fetch()还是fetchAll()?一点也不!如果代码被修改为使用fetch()而不是fetchaAll(),则该方法可能仍能正常工作,但测试将失败。@古墓,这是模拟依赖项的一般问题,而不仅仅是这个答案。