第一次用 PHPUnit 寫測試就上手

3. Data Providers (資料提供者)

資料提供者,能提供多筆的測試資料給測試案例進行多次的測試。

使用資料提供者,能讓測試更簡潔,因為,可以將測試的 assertions 與測試資料分開寫。

● 測試 3 – 限制報名人數

在一開始有提到,活動報名系統,會限制每個活動的報名人數。測試案例要測試多個不同報名人數的活動,如果報名成功,reserve() 會回傳 true,相反的報名失敗則回傳 false

src/PHPUnitEventDemo/Event.php

Event 類別的 reserve() 加入判斷,目前報名人數是否超過活動限制的報名人數,如果沒超過,User 物件加入到 $attendees 陣列內,回傳 true,超過的話,則回傳 false

tests/EventTest.php

 

EventTest 類別內,加入一個測試方法為 testAttendeeLimitReserve() 來測試限制報名人數。

  • testAttendeeLimitReserve() : 標註了 @dataProvider eventsDataProvider,會取得來自 eventsDataProvider() 的測試資料
  • eventsDataProvider() : 資料提供者,回傳了一個陣列,第一層陣列有兩個元素,表示有兩筆測試資料;第二層陣列有六個元素,表示每個資料傳到測試案例內為六個引數

eventsDataProvider() 的活動資料會由 testAttendeeLimitReserve() 接收,共會分別測試兩次,第一次的測試,會收到報名人數 5 個人的活動;第二次則是會收到報名人數 10 個人的活動。

testAttendeeLimitReserve() 測試案例內,會依來自 eventDataProvider() 的回傳值建立不同報名人數的 Event 物件,每個活動都會有 6 個不同的使用者報名,如果已經報名的人數還沒超過活動限制的報名人數,預期 Eventreserve() 方法的回傳值為 true,反之,超過活動限制的報名人數,則就會預期回傳 false

執行測試

從測試訊息可以看到,在 EventTest 測試中,有 3 個測試案例,但是測試結果跑了 4 個測試,為什麼呢?

因為 testAttendeeLimitReserve() 使用了 eventsDataProvider() 作為資料提供者,eventsDataProvider() 提供了兩筆資料,這兩筆資料會分別執行兩次測試,加上另外兩個測試案例,所以共有 4 個測試。

Data Provider 與 Test Dependency 的問題

先來看例子,再說明會造成的問題。

tests/EventTest.php

原本 testUnreserve() 是依賴 testReserve() ,試著將 testReserve() 改成依賴 testAttendeeLimitReserve(),而 testAttendeeLimitReserve() 使用了eventsDataProvider() 作為資料提供者。

接著,執行這個測試。

從測試結果可以看出來,在執行 testUnreserve() 測試案例的時候,無法取得 $event 物件,表示 testUnreserve() 根本沒取得來自 testAttendeeLimitReserve() producer 所回傳的值。

所以,在使用相依測試 (Test dependecy) 與資料提供者 (Data provider) 要特別注意,被相依的測試案例,是否有使用資料提供者。

4. Test Exceptions (異常測試)

開發的時候,除了要確保程式運作正常、功能有達到之外,也要對程式可能會超出正常執行的部分進行異常處理,而不是讓程式直接噴出錯誤訊息或忽然的運作停止,如果是這個情況通常都會丟出一個異常出來,讓程式能順暢的處理錯誤,所以,Test exceptions 主要是預期執行發生錯誤的時候,程式會丟出異常出來。

● 測試 4 – 防止重複報名

報名功能需要加入防止相同使用者重複報名相同的活動,如果重複報名的話,就會拋出一個異常出來,接下來的測試,會預期接收到重複報名的異常。

先撰寫要拋出的異常類別。

src/PHPUnitEventDemo/EventException.php

接下來撰寫拋出異常的實作。

src/PHPUnitEventDemo/Event.php

因為 Event$attendees 陣列,是用 User 物件 $id 為索引值,來儲存報名使用者的 User 物件。要判別使用者是否已經報名過相同的活動,只要報名的使用者 id 有存在 $attendees 陣列索引值,表示已經有報名活動,如果已報名活動,就會拋出例外。

tests/EventTest.php

 

EventTest 內增加一個 testDuplicatedReservationWithException() 測試案例,在註解內標註:

  1. @expectedException \PHPUnitEventDemo\EventException : 預期的異常類別
  2. @expectedExceptionMessage Duplicated reservation : 預期的異常訊息
  3. @expectedExceptionCode 1 : 預期的異常代碼

也就是,預期在這個測試案例內會接收到 EventException 的異常類別、異常訊息為 Duplicated reservation,異常代碼為 1。

執行測試

5. Fixtures

Fixture 能協助測試時,需要用到的測試環境、物件的建立,在測試完後,把測試環境、物件拆解掉,還原到初始化前的狀態。

主要透過 setUp()tearDown() 分別來初始化測試與拆解還原到初始化前的狀態。

下面一樣利用 test/EventTest.php 來示範,先了解目前測試有哪些問題。

tests/EventTest.php

注意 testReserve()testDuplicatedReservationWithException() 兩個測試案例,都需要在測試前建立 EventUser 物件,使用 setUp() 在測試前,建立兩個物件,測試完後,tearDown() 再把不需要的物件清空。

加入 fixtures 後

tests/PHPUnitEventDemo.php

$event$user 物件修改成全域變數,接著把建立物件寫在 setUp() 中,清空物件寫在 tearDown(),再將 原本 testReserve()testDuplicatedReservationWithException() 中的 建立 $event$user 物件程式移掉,且使用到這兩個變數改成使用全域變數,也就是 $this->event$this->user

所以在執行測試的時候,運作順序會是: setUp()testReserve()tearDown() → … → setUp()testDuplicatedReservationWithException()tearDown()

五、設定 PHPUnit

在前面此用 PHPUnit 工具來執行測試時,有用到 –bootstrap,在執行測試前先執行 vendor/autoload.php 程式來註冊 autoloading 的 function。可是每次執行測試,都要加上參數有點麻煩,所以,PHPUnit 可以利用 XML 設定檔來設定。

將 phpunit.xml 設定檔放在專案目錄下,與 src、tests 同一層。

phpunit.xml

 

  • <phpunit> : 加入 bootstrap 屬性,對應到的值就是要執行的程式檔案
  • <testsuites> : 在專案底下,能採用不同的測試組合。由一至多個的 <testsuite> 組成
  • <testsuite> : name 屬性,設定測試組合的名稱。測試組合內會包括許多測試程式檔案。

執行測試,如果 XML 設定檔檔名不是 phpunit.xml 的話,可以利用 --configuraton 來指定 XML 設定檔的路徑,如果檔名是 phpunit.xml ,就能省略不指定。

也可以執行不同的測試組合

還有更多 XML 設定檔可以使用,參考:https://phpunit.de/manual/current/en/appendixes.configuration.html

六、Code Coverage 分析

撰寫好單元測試之後,該如何了解到哪些目標程式還沒有經過測試?目標程式被測試百分比有多少?

PHPUnit 是利用 PHP_CodeCoverage 來計算程式碼覆蓋率 (Code coverage),需要安裝 Xdebug。

該如何產生 Code coverage 呢? 先在專案底下建立一個 reports/ 目錄,存放 Code coverage 分析的結果。

當然,也可以使用 XML 設定檔來設定。

phpunit.xml

接著執行測試

就可以在 reports/ 下打開 index.html 或其他 HTML 檔案,瀏覽 Code coverage 分析的結果。

enter image description here

更多資料

  • 範例程式:https://github.com/ymhuang0808/PHPUnit-Event-Demo
  • PHPUnit 安裝:https://github.com/ymhuang0808/Hands-On-Writing-Unit-Testing-With-PHPUnit/wiki
  • 參考資料:https://phpunit.de/documentation.html

此文章同步發表於 OpenFoundry:

 

第一次用 PHPUnit 寫測試就上手

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *