摘要
本文向你介紹測試驅動開發的概念,并用一個簡單的示例項目來做示范。
介紹
開發PHP產品有很多不同的方法。我們大多數傾向于從一個簡單的腳本開始,逐步向前推進。 或許我們可以預先列出我們的腳本,但是我們往往是停留在開發階段,在需要測試的時候不會真正的去開始測試?;旧?,我們是先開發后測試。
但是這樣做或許不是最好的辦法,可能會在今后帶來問題。這就是為什么一些開發者提倡一種不同的開發方式,叫做測試驅動開發(TDD)的原因- 就是先測試后開發。
你可能會疑惑這樣該怎么做,而這正是本文將討論的。我將帶領你們通過一個真實的簡單項目去示范TDD如何工作。本文和示例項目都基于Noel Darlow(“McGruff”)在論壇中向另一個論壇成員演示TDD如何工作的討論。
我們的示例項目是一個Biter類,它通過使用正則表達式可以“咬掉”字符串中的片段,就象這樣:
bite ('/pattern/');?>
我們的類也將修改原始的字符串,把匹配的部分去除(所以我們叫它“吞噬者”)。
讓我們從設置測試框架開始。
設置測試框架
由于我們從測試出發,我們需要有一些測試框架。我將使用SimpleTest 框架,僅僅因為我最熟悉它。
下載一份SimpleTest的拷貝,把它安裝在你本機或者你的服務器上。然后創建一個叫做"test_biter.php"的新文件,里面寫下面的代碼:
require_once 'simpletest/unit_tester.php';
require_once 'simpletest/reporter.php';
class BiterTestCase extends UnitTestCase {
function testSetup () {
$this->assertTrue(false);
}
}
$test = new BiterTestCase('TDD Biter Test');
$test->run(new HtmlReporter());
?>
讓我們分析一下這個例子。首先我們包含了一些SimpleTest框架的文件(你要確認一下路徑是否和你的一樣)。接著我們建立了一個叫做BiterTestCase的新類,它將用來測試我們的Biter類。象你看到的一樣,BiterTestClase類繼承于UnitTestCase類,這個意味著BiterTestClass是我們第一個真正意義上的測試用例。
BiterTestClass類只有一個方法調用叫做'testSetup'。任何以“test”開頭的方法都會被SimpleTest框架自動執行,因此它們應該是被用來測試項目中的某個部分。在上面的例子中,我們通過調用assertTrue()方法來確認框架被正確設置。
例子中的后面兩行是建立一個測試用例的實例,然后運行所有測試。如果所有設置都正確的話,你會得到下面的輸出:
現在我們已經設置好了測試框架并正常運行。讓我們開始我們的Biter類。
第一個測試
在我們測試Biter類的任何功能之前,我們必須確認這個類存在。我們當然也是寫一個測試來做這件事:
require_once 'simpletest/unit_tester.php';
require_once 'simpletest/reporter.php';
class BiterTestCase extends UnitTestCase {
function testClassExists() {
$this->assertTrue(class_exists('Biter'), 'Biter class exists');
}
}
$test = new BiterTestCase('TDD Biter Test');
$test->run(new HtmlReporter());
?>
現在運行上面的測試。不要創建類或者包含其他內容;先運行測試。由于'Biter' 類不存在,你會看到下面的輸出:
如你所見,我們的測試指出'Biter'類不存在。在TDD中,你總是從一個失敗的測試開始工作。
現在我們必須讓測試能夠通過,做到這點需要創建Biter類。建立一個叫'biter.php'的新文件,寫入如下代碼:
Class Biter {
}
?>
然后把Biter類包含到我們的測試文件,加入如下行:
require_once 'biter.php';
現在再運行測試。這次它們會通過了,輸出如下:
在創建Biter類的時候,我們走出了TDD的第一步。首先我們創建測試,然后再做實際的開發。
在目前我們的Biter類還沒有任何功能,下面讓我們開始我們的Biter類的第一部分:“開始吞噬”。
Biter返回正確的匹配嗎?
我們希望第一步明確的是當我們調用bite()方法的時候Biter類將返回正確的匹配,因此,讓我們就此寫一個測試:
function testBiteReturnsAPatternMatch() {
$biter =& new Biter;
$haystack = 'foobar';
$this->assertEqual(
$biter->bite('/foo/', $haystack),
'foo',
'Biter returns correct match');
}
這個測試確認biter返回正確的匹配。它使用了assertEqual()方法,從名字我們就能知道這個方法的意思。把測試增加到我們的測試集,然后再運行測試。它會出錯甚至返回一個致命錯誤,因為我們還沒提供bite()方法。你會看到下面的輸出: