我們經常會遇到這種情況:將一些沒有經過任何測試的遺留代碼進行重新編寫測試,甚至這些代碼還是用物件導向寫的。要對這樣的代碼進行測試,我的建議是把代碼分解成塊,這樣就容易測試了。
然而,這些遺留代碼並不是那麼好重構的,比如:測試前,你不能在把代碼重新編寫,這是為了避免影響原有程式,當然也不好進行單元測試。
在PHP程式中,通常有一部分代碼是寫在幾個index.php和script.php檔案中的,這些.php檔案存放在幾個不同的檔案夾裡。如果不找到它們的進入點,是無法直接由Web伺服器訪問的。
測試副本
要測試一個PHP指令碼,我們需要類比一個HTTP請求,並檢查返回的響應(response)是否等於預期值。這裡需要注意的是類比一個請求,要定義response和request,這不僅僅是內容(content)的不同,而且他們的頭資訊(header)也是不同的。
此外,如果我們想要測試一個操作資料的事務指令碼,我們要確保不讓它去串連真正的資料庫或應用程式的其餘部分。
在現實中,通常沒有人會直接拿原有的PHP指令碼進行重寫測試。因為怕把代碼弄得不可恢複。我建議使用PHP指令碼的副本,這樣我們就可以將PHP代碼進行一些小手術了。
如何將代碼進行最小修改:刪除include和require語句(如果它們沒有被用到),並且修改內建函式的調用方式,例如:將header()寫成$object->header()。
最後,我們來測試這個事務指令碼。測試完後,我們可以從副本指令碼中提取出它們,並把它們放入新指令檔中。
具體步驟
一、類比一個HTTP請求並重新定義變數$_GET和$_POST,還要修改$_SERVER的header。
二、擷取請求響應,response的body可以通過ob_start()和ob_get_clean()捕獲,它可以收集每一個用echo()或以
注意:輸出緩衝支援在PHP多個層級的嵌套,所以在大多數情況下,都可以捕獲到,即使指令碼在使用ob_*調用本身。
三、測試指令碼應包含事務指令碼的內部方法,因此在這個指令碼範圍內的方法都可以被調用。例如:
1.指令碼所需的變數可以被定義為局部變數封裝起來,如$connection作為一個資料庫連接。
2.不是原本PHP的內建函數,應該加上對象來調用,如:header()寫成$this->header()。
具體代碼
這就是我們要測試的事務指令碼對象,具體到指令碼中,我們還需要封裝:
<?php
class ForumPosting
{
private $headers = array();
public function handleRequest($postRequest)
{
$_POST = $postRequest;
$connection = $this->getAConnection();
ob_start();
include 'forum/post_new_copy.php';
$content = ob_get_clean();
return array(
'content' => $content,
'headers' => $this->headers
);
}
private function header($headerLine)
{
$this->headers[] = $headerLine;
}
...
}
這是我們的測試代碼:
public function testANewPostIsCreated()
{
$action = new ForumPosting();
$response = $action->handleRequest(array(
'id_thread' => 42,
'text' => 'Hello, world',
...
));
$this->assertEquals('...', $response['content']);
$this->assertContains('Content-type: text/html', $response['headers']);
}
結論
測試副本只是暫時的!它讓我們編寫的測試不會改變。最終,我們要將已經通過測試的PHP指令碼進行重構,以消除冗餘代碼。
當我們的測試完成後,可以將handleRequest()的內容替換成真正的邏輯代碼。假如你要寫很多這樣的測試指令碼,你可以寫一個通用的測試對象,以滿足你的測試需要。