2011-03-17 121 views
8

我試圖用PHPUnit單元測試映射類。 我可以輕鬆地模擬將被注入到mapper類中的PDO實例,但我無法弄清楚如何模擬PreparedStatement類,因爲它是由PDO類生成的。PHPUnit - 如何模擬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))); 
} 

...它仍然是一個靜態的instanciation,因爲我無法通過準備好的語句類的我自己的嘲笑實例。

有什麼想法?

編輯: 解決方案,改編自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); 
} 

回答

10

如果你可以嘲笑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()); 


    } 

} 
+0

準確地說,你確實發現了我沒有太明白的部分:當你可以僞造fetch時,不需要注入語句,否則返回的東西可以在測試中決定。我編輯了我的問題來放置我的改編解決方案。謝謝 ! – FMaz008 2011-03-18 14:53:29

+1

這種方法的問題是它將應用程序代碼實現暴露給測試。測試應該知道應用程序是否正在調用 - > fetch()或fetchAll()?一點也不!如果代碼適用於使用fetch()而不是fetchaAll(),該方法仍然可以正常工作,但測試將失敗。 – 2013-03-08 17:26:28

+1

@TomB,這是嘲笑依賴性的一個普遍問題,而不僅僅是這個答案。 – PeerBr 2014-06-10 14:59:25