PHPUnit-如何模拟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

我正在尝试使用PHPUnit对映射器类进行单元测试。 我可以轻松地模拟将被注入mapper类的PDO实例,但我不知道如何模拟PreparedStatement类,因为它是由PDO类生成的

在我的例子中,我扩展了PDO类,因此我有以下内容:

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(),则该方法可能仍能正常工作,但测试将失败。@古墓,这是模拟依赖项的一般问题,而不仅仅是这个答案。