PHPUnit - Как смоделировать PDO подготовленный оператор - PullRequest
8 голосов
/ 17 марта 2011

Я пытаюсь провести модульное тестирование класса mapper с помощью PHPUnit. Я могу легко смоделировать экземпляр PDO, который будет внедрен в класс mapper, но я не могу понять, как смоделировать класс 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)));
}

Дело в том, что Core_Db_Driver_PDOStatement не внедряется в конструктор класса 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)));
}

... это все еще статичный экземпляр, так как я не могу передать свой собственный смоделированный экземпляр подготовленного класса операторов.

Есть идеи?

Edit: Решение, адаптированное от ответа:

/**
 * @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);
}

1 Ответ

10 голосов
/ 17 марта 2011

Если вы можете издеваться над классом PDO, просто смоделируйте класс pdo и все его зависимости.Не нужно заботиться о классе операторов или конструкторе класса pdo, так как вы определяете ввод и вывод через макеты.

Поэтому вам нужен фиктивный объект, который возвращает фиктивный объект.

Это может показаться немного запутанным, но, так как вам нужно только протестировать, что делает тестируемый класс, и ничего больше, вы можете в значительной степени убрать все остальные части вашего соединения с БД.

В этом примере всеВы хотите выяснить это:

  • Вызывается ли подготовка?
  • Вызывается ли 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());


    }

}
...