第一次用 phpunit 寫測試就上手
TRANSCRIPT
第⼀一次⽤用 PHPUnit 寫測試就上⼿手
本著作除有特別註明外,係採⽤用創⽤用 CC 姓名標⽰示-相同⽅方式分享 4.0 國際 授權條款授權
2014/12/24⿈黃儀銘 [email protected]
⼤大綱
‣ 介紹 unit testing
‣ 簡介 PHPUnit
‣ PHPUnit 提供的功能
- Assertion, Test Dependencies,
- Data Providers, Test Exception, Fixtures
‣ 組織及設定 PHPUnit
‣ Code Coverage
reserve($user)
unreserve($user)
...
Event
...
testReserve()
testUnreserve()
...
EventTest
執⾏行測試
測試結果
對程式中最⼩小單位 (unit) 進⾏行測試
如 function 或 class 中的 method
function reserve($user){ // 使⽤用者報名此活動}
public function testReserve(){ // 使⽤用者報名此活動 $this->assertCount($expectedCount, $this->event->attendees);}
⺫⽬目標程式 測試程式
function reserve($user){ // 使⽤用者報名此活動}
public function testReserve(){ // 使⽤用者報名此活動 $this->assertCount($expectedCount, $this->event->attendees);}
測試 function 或 method 是否有
達成預期中的功能
測試報名⼈人數是否符合預期
⺫⽬目標程式 測試程式
Unit testing 的重要性
確保單元的執⾏行結果
儘早發現程式中的錯誤
修改程式,更加有信⼼心
測試即⽂文件此⾴頁 4 個圖⽰示作者皆為 Paomedia 採⽤用CC 姓名標⽰示 3.0
來源:https://www.iconfinder.com/paomedia
public function reserve($user) { // 使⽤用者報名
if (array_key_exists($user->id, $this->attendees)) { throw new \PHPUnitEventDemo\EventException( 'Duplicated reservation', \PHPUnitEventDemo\EventException::DUPLICATED_RESERVATION ); }
if ($this->attendee_limit > $this->getAttendeeNumber()) { $this->attendees[$user->id] = $user;
return true; }
return false; }
測試即⽂文件
⺫⽬目標程式
測試即⽂文件public function testReserve(){ // 測試報名活動} /** * @dataProvider eventsProvider */public function testAttendeeLimitReserve(...){ // 測試限制報名⼈人數}
/** * @expectedException \PHPUnitEventDemo\EventException * @expectedExceptionMessage Duplicated reservation * @expectedExceptionCode 1 */public function testDuplicatedReservationWithException(){ // 測試重複報名異常}
1. 容易撰寫及維護 public function testReserveAndUnreserve() { // 測試參加活動 $userId = 1; $userName = 'A User'; $user = new \PHPUnitEventDemo\User($userId, $userName); $eventId = 1; $eventName = 'Demo Event'; $eventStartDate = '2014-11-30 13:30:00'; $eventEndDate = '2014-11-30 16:30:00'; $eventDeadline = '2014-11-29 23:59:59'; $eventAttendeeLimit = 15; $event = new \PHPUnitEventDemo\Event( $eventId, $eventName, $eventStartDate, $eventEndDate, $eventDeadline, $eventAttendeeLimit ); $event->reserve($user); $expectedCount = 1; $this->assertCount($expectedCount, $event->attendees); $this->assertContains($user, $event->attendees); $event->unreserve($user); $this->assertCount(0, $event->attendees); $this->assertNotContains($user, $event->attendees); }
建⽴立物件
預期判斷
✗
public function testReserveAndUnreserve() { // 測試參加活動 $userId = 1; $userName = 'A User'; $user = new \PHPUnitEventDemo\User($userId, $userName); $eventId = 1; $eventName = 'Demo Event'; $eventStartDate = '2014-11-30 13:30:00'; $eventEndDate = '2014-11-30 16:30:00'; $eventDeadline = '2014-11-29 23:59:59'; $eventAttendeeLimit = 15; $event = new \PHPUnitEventDemo\Event( $eventId, $eventName, $eventStartDate, $eventEndDate, $eventDeadline, $eventAttendeeLimit ); $event->reserve($user); $expectedCount = 1; $this->assertCount($expectedCount, $event->attendees); $this->assertContains($user, $event->attendees); $event->unreserve($user); $this->assertCount(0, $event->attendees); $this->assertNotContains($user, $event->attendees); }
測試多個功能
2. ⼀一個測試案例只測試單⼀一功能
✗
活動報名系統⺫⽬目錄結構
. |-- src
| '-- PHPUnitEventDemo
| '-- Event.php
| '-- User.php
|
'-- tests
'-- EventTest.php
Assertions
‣assertTrue(true); # SUCCESSFUL‣assertEquals('orz', 'oxz', 'The string is not equal with orz'); # UNSUCCESSFUL‣assertCount(1, array('Monday')); # SUCCESSFUL‣assertContains('PHP', array('PHP', 'Java', 'Ruby')); # SUCCESSFUL
還有更多assertions ,請參考http://goo.gl/YmoaE9
驗證執⾏行結果是否為預期
範例
加⼊入相依後 被相依的測試失敗後,相依的測試不會執⾏行
Test Dependencies
未加⼊入相依測試 加⼊入相依測試
testReserve() Fail Fail
testUnreserve() Run Skipped
Data Providers
‣ 提供測試資料給測試⽅方法 ‣ data provider 必須為 public
‣ 回傳 array 或 iterator
- ⾃自訂 iterator,CSV, XML…
$ phpunit --bootstrap vendor/autoload.php tests/EventTestPHPUnit 4.3.5 by Sebastian Bergmann.
...PHP Fatal error: Call to a member function unreserve() on a non-object in /Users/aming/git/PHPUnit-Event-Demo/tests/EventTest.php on line 114
...
Fatal error: Call to a member function unreserve() on a non-object in /Users/aming/git/PHPUnit-Event-Demo/tests/EventTest.php on line 114
...
造成 testUnreserve(),無法取得傳⼊入的物件
Depends 與 Data Provider
Test Exceptions
‣ 預期測試⽅方法內拋出異常 ‣ 利⽤用 3 個標註:
- @expectedException ExceptionClass
- @expectedExceptionMessage ExceptionMessage
- @expectedExceptionCode ExceptionCode
Fixtures
‣ 提供測試狀態初始化及回復到原來狀態 ‣ setUp() 初始化設定物件或變數等
‣ tearDown() 還原成原來狀態 ‣ 執⾏行順序
- 測試⽅方法執⾏行前會呼叫setUp() - 測試⽅方法執⾏行後,不論成功或失敗都會呼叫tearDown()
testReserve()
setUp()
tearDown()
Fixtures
EventTest 執⾏行順序
setUp()
testDuplicatedReserve()
tearDown()
Unit testing 的侷限
程式不會沒有 bugs
圖⽚片來源:http://runningintherealworld.wordpress.com/2011/06/23/one-to-go/
組織及設定 PHPUnit
bootstrap 設定
<phpunit bootstrap="./vendor/autoload.php"> <testsuites> <testsuite name="MyEventTests"> <file>./tests/EventTest.php</file> </testsuite> </testsuites></phpunit>
phpunit.xml
組織及設定 PHPUnit
<phpunit bootstrap="./vendor/autoload.php"> <testsuites> <testsuite name="MyEventTests"> <file>./tests/EventTest.php</file> </testsuite> </testsuites></phpunit>
phpunit.xml
多個 testsuite 的集合
組織及設定 PHPUnit
<phpunit bootstrap="./vendor/autoload.php"> <testsuites> <testsuite name="MyEventTests"> <file>./tests/EventTest.php</file> </testsuite> </testsuites></phpunit>
phpunit.xml
testsuite 要測試的檔案或資料夾
<phpunit bootstrap="./vendor/autoload.php"> <testsuites> <testsuite name="MyEventTests"> <file phpVersion="5.1.0" phpVersionOperator=“>="> ./tests/EventTest.php </file> </testsuite> </testsuites></phpunit>
phpunit.xml
組織及設定 PHPUnit
執⾏行測試需要的版本
組織及設定 PHPUnit
$ phpunit --configuration phpunit.xml --testsuite MyEventTestsPHPUnit 4.3.5 by Sebastian Bergmann.
Configuration read from /Users/aming/git/PHPUnit-Event-Demo/phpunit.xml
......
Time: 5.82 seconds, Memory: 12.75Mb
OK (6 tests, 20 assertions)
Generating code coverage report in HTML format ... done
--configuration <config_file>phpunit 讀取設定檔
Code Coverage
$ phpunit --bootstrap vendor/autoload.php --coverage-html report/ tests/PHPUnit 4.3.5 by Sebastian Bergmann.
.........
Time: 3.49 seconds, Memory: 13.25Mb
OK (9 tests, 17 assertions)
Generating code coverage report in HTML format ... done
phpunit tool 中設定 --coverage-html <dir>
Code Coverage
phpunit.xml 中設定<phpunit bootstrap="./vendor/autoload.php"> <testsuites> <!-- ignore --> </testsuites> <logging> <log type="coverage-html" target="report/" charset="UTF-8"/> </logging></phpunit>
還有更多設定參考:https://phpunit.de/manual/current/en/appendixes.configuration.html#appendixes.configuration.logging
More information
‣ PHPUnit official site - https://phpunit.de/
‣ 《PHPUnit Essentials》- ISBN 978-1-78328-343-9
‣ xUnit Patterns - http://xunitpatterns.com/
‣ Demo 程式碼 - http://goo.gl/cEYfv4
‣ 練習專案 - http://goo.gl/9bFnWJ
‣ 練習環境架設 - http://goo.gl/ppBlz1