假設我想測試一個簡單的助手,它將類名稱作爲參數並進行重定向。使用PHPUnit測試幫助函數
我該如何測試這個功能是否在很多控制器內被調用?我應該測試在整個代碼中作爲參數傳遞的每個類名(將它們自己寫在提供者函數中)?還是有一個神奇的功能,這對我來說呢?
假設我想測試一個簡單的助手,它將類名稱作爲參數並進行重定向。使用PHPUnit測試幫助函數
我該如何測試這個功能是否在很多控制器內被調用?我應該測試在整個代碼中作爲參數傳遞的每個類名(將它們自己寫在提供者函數中)?還是有一個神奇的功能,這對我來說呢?
你的問題是爲什麼依賴注入 - 當完成正確(不是大多數流行的框架如何實現它) - 被吹捧爲代碼可測試性的終極目的。
要理解爲什麼,讓我們看看「助手函數」和麪向類的編程如何讓您的控制器難以測試。
class Helpers {
public static function myHelper() {
return 42;
}
}
class MyController {
public function doSomething() {
return Helpers::myHelper() + 100;
}
}
單元測試的整個要點是驗證代碼的「單元」是否獨立工作。如果你不能分離功能,你的測試是沒有意義的,因爲它的結果可能會受到其他代碼行爲的影響。這可能會導致統計學家稱爲類型I和類型II錯誤:基本上,這意味着您可以獲得可能對您說謊的測試結果。
在上面的代碼中,助手不能被輕易地模擬,以確定MyController::doSomething
工作在與外界影響完全隔離。爲什麼不?因爲我們不能「嘲笑」幫助器方法的行爲,以保證我們的doSomething
方法實際上將100添加到幫助器結果中。我們堅持幫助者的確切行爲(返回42)。這是一個正確的面向對象和控制反轉完全消除的問題。讓我們考慮如何一個例子:
如果MyController
要求對於它的依賴,而不是使用靜態輔助功能,它變得微不足道嘲弄外界的影響。試想一下:
interface AnswerMachine {
public function getAnswer();
}
class UltimateAnswerer implements AnswerMachine {
public function getAnswer() {
return 42;
}
}
class MyController {
private $answerer;
public function __construct(AnswerMachine $answerer) {
$this->answerer = $answerer;
}
public function doSomething() {
return $this->answerer->getAnswer() + 100;
}
}
現在,它是平凡簡單的測試MyController::doSomething
事實上確實增加100無論它從應答機回來:
// test file
class StubAnswerer implements AnswerMachine {
public function getAnswer() {
return 50;
}
}
$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);
這個例子還演示瞭如何正確使用接口在你的代碼中可以大大簡化測試過程。像PHPUnit這樣的測試框架可以非常容易地模擬接口定義,以便準確執行您希望它們以測試代碼單元的獨立功能。
所以我希望這些非常簡單的例子說明在測試代碼時依賴注入的強大程度。但更重要的是,我希望他們能證明爲什麼如果你的選擇框架使用靜態(只是另一個全局名稱),單例和輔助函數,你應該保持警惕。
你不能測試每個可能的參數組合到你需要測試的所有功能;它會比你的宇宙生命更長久。所以你使用人類智能(有些人可能稱之爲作弊;-)。測試一次,在這種情況下,以模擬控制器作爲參數。
然後看看你的代碼,並問問自己是否有其他傳入的對象真的會有不同的行爲。對於你描述爲「簡單幫手」的東西,答案可能不是。但是,如果是的話,怎麼樣?創建另一個模擬控制器類來模擬不同的行爲。例如。這第二個控制器可能沒有助手類期望調用的函數。您期望拋出異常。爲此創建單元測試。
重複,直到滿意。