繼第一部份《Working with PHPUnit3, part 1 - 安裝備忘錄》後,接下來舉例說明操作 PHPUnit 之過程。
繼第一部份《Working with PHPUnit3, part 1 - 安裝備忘錄》後,接下來舉例說明操作 PHPUnit 之過程。
PHPUnit3 主要指令工具是 phpunit ,請瀏覽《Chapter 5. The Command-Line Test Runner》查看 phpunit 指令說明。此處概述一些注意事項。首先, PHPUnit3 以 class 為測試單位,要求每一測試案例 (Test case, Test story) 之內容應實作在一個類別中。為與測試對象有所區別,本文稱實作測試案例內容之類別為測試類別 (test class) 。據此, phpunit 的第一參數為測試類別的名稱,並假設每一個 php 測試案例源碼檔僅包含一個測試類別,且測試案例之源碼檔名與測試類別名稱相同。 phpunit 又遵循 PEAR Coding Standards::Naming Conventions 為 php 源碼檔命名與配置原則,將底線字元 (_) 視為檔案系統目錄層級的分隔字元。例如測試類別名稱若為 Model_My_ExampleTest ,則 phpunit 會自動在檔案系統中找尋 'Model/My/ExampleTest.php' 。若要抑止此一預設檔案規則,則可以指定第二參數為 php 源碼路徑與檔名。
$ phpunit Model_My_ExampleTest # 僅指定第一參數, phpunit 自動尋找 Model/My/ExampleTest.php $ phpunit Model_My_ExampleTest unit_test/my_example_test.php # 指定 phpunit 以 unit_test/my_example_test.php 為測試案例源碼檔
SimpleTest.php 是一個基本的測試案例源碼樣板,可以此測試 PHPUnit3 是否成功安裝。
以下列出 PHPUnit3 撰寫測試案例源碼時的基本事項:
@test。因此 SimpleTest.php 中只有 testItIsTrue() 及 arrayIsEmpty() 是測試項目,而 thisIsNotATest() 不會執行。
$ phpunit --testdox-text test_list.txt SimpleTest PHPUnit 3.0.0 by Sebastian Bergmann. .. Time: 00:00 OK (2 tests) $ type test_list.txt Simple - It is true - Array is empty
由於測試案例源碼內容大致相同, phpunit 也提供了自動產生測試案例源碼骨架的功能,根據測試對象的內容產生一個對應的測試案例源碼。請參閱《Chapter 16. Skeleton Generator》,測試對象必須是一個類別,若 php 源碼檔中包含多個類別時, phpunit 只將最後一個類別視為測試對象而為其產生測試案例源碼骨架。
DatabaseRow 是一個彈性的資料庫欄位類別。可以藉 Configuration-driven Development (See also: 《Example of Configuration Driven Development with PHP》) 的方式讀入資料庫欄位的 schema ,並能檢查資料值是否符合資料型態。它覆載了 __set, __get 行為作欄位內容的存取子。可隨時新增欄位,唯新增欄位不檢查資料型態。此外,對於嘗試取得未定義欄位內容的動作,皆回傳 False 。
以 DatabaseRow 類別為測試對象,接著使用 phpunit 的選項 --skeleton 為 DatabaseRow 產生測試案例源碼骨架。 phpunit 會在測試對象之名稱後添加 'Test' 作為測試案例源碼檔與測試類別之名稱。 在 team-work 時,通常我們在決定類別的各項公開行為的名稱後就會產生測試案例骨架,接著就將工作類別與測試類別分別交給程式人員和測試人員撰寫,不必等到類別的內容實作完成後才產生測試案例骨架。
$ phpunit --skeleton DatabaseRow PHPUnit 3.0.0 by Sebastian Bergmann. Wrote test class skeleton for "DatabaseRow" to "DatabaseRowTest.php".
請自行開啟 DatabaseRowTest.php 查看測試案例骨架源碼。 phpunit 產生的測試案例骨架源碼會自動為測試對象之每一公開行為產生一個「未完成 (incomplete)」的測試項目。而且它的內容比較多,允許直接執行。
$ phpunit DatabaseRowTest PHPUnit 3.0.0 by Sebastian Bergmann. II Time: 00:00 OK, but incomplete or skipped tests! Tests: 2, Incomplete: 2. $ php DatabaseRowTest.php PHPUnit 3.0.0 by Sebastian Bergmann. II Time: 00:00 OK, but incomplete or skipped tests! Tests: 2, Incomplete: 2.
當我們將 DatabaseRow 類別的內容完成到一個段落時,我們同時改寫 DatabaseRowTest.php 的內容,為了載入 schema ,我添加 phpunit 沒有為我們加上的建構行為,在其中載入 schema (30-34行)。此處我先測試未指派 schema 和有指派 schema 兩個情形。下列為測試案例使用的 schema 與改寫後的第二版 DatabaseRowTest.php 。
請自行執行並觀察結果。接著我要測試設定欄位值的動作,由於每一個測試項目執行前都需要建立一個乾淨的 DatabaseRow 實例,故我將建立 DatabaseRow 實例的動作寫在 setUp() 中。 setUp() 是專門用於為每一個測試項目建立乾淨的測試環境之用。See also:《Chapter 7. Fixtures》。下列為第三版的 DatabaseRowTest.php 。
最後示範以 phpunit 在測試同時為我們產生測試項目清單。此處就可看出為測試案例類別的實例行為 (即測試項目) 取一個有意義的名字相當重要。在 Agile method 中隨處可見「source code is document」這句話。追隨它,相信它,貫徹它。不要淪為報告打字員。我們堅持拒絕多打一次報告! (See also: 《軟體工程三大陣營, RUP, CMMI, Agile Method》)
$ phpunit --testdox-text test.log DatabaseRowTest PHPUnit 3.0.0 by Sebastian Bergmann. ..... Time: 00:00 OK (5 tests) $type test.log DatabaseRow - New database row without schema - New database row with schema - Set id as invalid value - Set id as five - Set name as john
我在《先說故事再動手設計, 從一個簡單故事看 Test Driven Development》也說明了如何在日常工作中推動 Test-driven Development 過程。到目前為止已經介紹了相當多 PHPUnit3 的內容,基本上足以應付多數場合的需求。更多的內容可以參閱線上手冊《PHPUnit Pocket Guide》。