2008-12-05 135 views
4

我想測試一個管理數據庫中數據訪問的類(你知道,CRUD,本質上)。我們正在使用的數據庫庫恰好有一個API,其中您首先通過靜態調用獲取表格對象:模擬PHPUnit中的對象來模擬靜態方法調用?

function getFoo($id) { 
    $MyTableRepresentation = DB_DataObject::factory("mytable"); 
    $MyTableRepresentation->get($id); 
    ... do some stuff 
    return $somedata 
} 

...您明白了。我們試圖測試這個方法,但是嘲笑DataObject的東西,這樣(a)我們不需要一個實際的db連接來測試,並且(b)我們甚至不需要包含DB_DataObject lib進行測試。

但是,在PHPUnit中,我似乎無法獲得$ this-> getMock()來正確設置靜態調用。我有...

 $DB_DataObject = $this->getMock('DB_DataObject', array('factory')); 

...但測試仍然說未知的方法「工廠」。我知道它正在創建該對象,因爲在它表示無法找到DB_DataObject之前。現在可以。但是,沒有辦法?

我真正想要做的是有兩個模擬對象,一個用於返回表格對象。因此,我不僅需要指定工廠是靜態調用,還要返回我已經設置的一些指定的其他模擬對象。

我應該提到一個警告,我在SimpleTest前段時間做了這個(找不到代碼),它工作正常。

什麼給?

[更新]

我開始掌握它是與預計()

回答

2

我同意你們倆最好不要使用靜態調用。但是,我想我忘了提及DB_DataObject是第三方庫,並且靜態調用是他們的代碼使用的最佳實踐,而不是我們的。還有其他方法可以使用它們的對象,這些方法包括直接構建返回的對象。它只是在那些使用那個DB_DO類的類文件中留下那些晦澀的include/require語句。這很糟糕,因爲如果你同時試圖在你的測試中嘲笑一個同名的班級,測試將會中斷(或者不會被隔離) - 至少我認爲。

1

這是你的代碼的依賴關係的一個很好的例子 - 在設計使它不可能注入在模擬而不是真正的課堂上。

我的第一個建議是嘗試重構代碼以使用實例而不是靜態調用。

0

從你的DB_DataObject類中遺漏了什麼(或不是?)是一個設置器,在調用factory方法之前傳遞一個準備好的db對象。這樣,如果需要的話,你可以傳遞一個模擬或自定義數據庫對象(使用相同的接口)。

在測試設置:

public function setUp() { 
     $mockDb = new MockDb(); 
     DB_DataObject::setAdapter($mockDb); 
} 

工廠()方法應該返回嘲笑的數據庫實例。如果它尚未集成到您的類中,您可能還必須重構factory()方法以使其工作。

0

您是否需要/在您的測試用例中包含DB_DataObject的類文件?如果在PHPUnit嘗試模擬對象之前該類不存在,則可以獲得像這樣的錯誤。

2

當您無法更改庫時,請更改您的訪問權限。重構爲DB_DataObject ::廠()的所有調用實例方法在你的代碼:

function getFoo($id) { 
    $MyTableRepresentation = $this->getTable("mytable"); 
    $MyTableRepresentation->get($id); 
    ... do some stuff 
    return $somedata 
} 

function getTable($table) { 
    return DB_DataObject::factory($table); 
} 

現在你可以使用你正在測試的類的部分模擬,並有getTable()返回一個模擬表對象。

function testMyTable() { 
    $dao = $this->getMock('MyTableDao', array('getMock')); 
    $table = $this->getMock('DB_DataObject', ...); 
    $dao->expects($this->any()) 
     ->method('getTable') 
     ->with('mytable') 
     ->will($this->returnValue($table)); 
    $table->expects... 
    ...test... 
}