第一次用 phpunit 寫測試就上手

68
第次 PHPUnit 寫測試就上 本著作除有特別註明外,係採創 CC 姓名標-相同式分享 4.0 國際 授權條款授權 2014/12/24 儀銘 [email protected]

Upload: yi-ming-huang

Post on 25-Jan-2017

604 views

Category:

Software


1 download

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

測試是什麼?為何如此重要?

限制報名⼈人數

你會如何測試呢?

1. 建⽴立 20 個以上的使⽤用者 2. 分別以這些使⽤用者的⾝身份報名活動

1. 建⽴立 20 個以上的使⽤用者 2. 分別以這些使⽤用者的⾝身份報名活動

崩╰(〒⽫皿〒)╯潰這不是測試!

3 與其他程式混在⼀一起測試

2 改程式碼後,需要再做⼀一次測試

可能遇到的問題

1 測試案例多時,花太多時間

讓我們把時間倒回開發時

reserve($user)

unreserve($user)

...

Event

...

testReserve()

testUnreserve()

...

EventTest

執⾏行測試

測試結果

Unit Testing

對程式中最⼩小單位 (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(){ // 測試重複報名異常}

怎樣才是好的 Unit testing

容易撰寫及維護1⼀一個測試案例只測試單⼀一功能2執⾏行時間不要過慢3持續更新測試4

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. ⼀一個測試案例只測試單⼀一功能

3. 執⾏行時間不要過慢

測試 撰寫程式

測試失敗

執⾏行測試

來回多次

4. 持續更新測試

測試 撰寫程式

測試失敗

執⾏行測試

修改測試

執⾏行測試 原本程式⾏行為改變或 新增刪除功能

1

2

PHPUnit

‣ xUnit 架構的實例

‣ PHP 單元測試 (unit testing) framework

PHPUnit 提供的功能

‣ Assertions

‣ Test Dependencies

‣ Data Providers

‣ Test Exceptions

‣ Fixtures

範例程式

活動報名系統

供使⽤用者報名及取消報名

活動報名系統

‣ 活動的資訊有包括 - id

- name

- start and end date

- deadline

- attendee limitation

- attendees

活動報名系統

‣ 使⽤用者的資訊有包括 - id

- name

- email

活動報名系統⺫⽬目錄結構

. |-- src

|  '-- PHPUnitEventDemo

|   '-- Event.php

|   '-- User.php

|

'-- tests

'-- EventTest.php

範例流程說明

撰寫類別介⾯面 撰寫測試

測試

撰寫實作

1

2

3

PHPUnit 提供的功能

‣ Assertions

‣ Test Dependencies

‣ Data Providers

‣ Test Exceptions

‣ Fixtures

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

驗證執⾏行結果是否為預期

範例

預期結果

1.符合的報名⼈人數

2.報名的名單中有已經報名的使⽤用者

測試 1 提供使⽤用者報名

測試 1 提供使⽤用者報名

預期結果

1.符合的報名⼈人數

2.報名的名單中有已經報名的使⽤用者

預期結果

1.符合的報名⼈人數

2.取消報名的使⽤用者不在報名的名單中

測試 2 提供取消報名

測試 2 提供取消報名

預期結果

1.符合的報名⼈人數

2.取消報名的使⽤用者不在報名的名單中

PHPUnit 提供的功能

‣ Assertions

‣ Test Dependencies

‣ Data Providers

‣ Test Exceptions

‣ Fixtures

Test Dependencies

‣ 對有依賴關係的測試⽅方法進⾏行測試 ‣ 允許 producer (被相依的測試)

將值傳給 consumer

加⼊入相依後 被相依的測試失敗後,相依的測試不會執⾏行

Test Dependencies

未加⼊入相依測試 加⼊入相依測試

testReserve() Fail Fail

testUnreserve() Run Skipped

PHPUnit 提供的功能

‣ Assertions

‣ Test Dependencies

‣ Data Providers

‣ Test Exceptions

‣ Fixtures

測試 3 限制報名⼈人數

預期結果

1.報名⼈人數已滿,使⽤用者無法報名

2.報名⼈人數未滿,使⽤用者可以完成報名

測試 3 限制報名⼈人數

欲測試的多個不同的活動

測試 3 限制報名⼈人數

這樣寫有⼀一些問題

如果想測試 50 個活動?活動資料跟測試寫在⼀一起,難閱讀!

Data Providers

‣ 提供測試資料給測試⽅方法 ‣ data provider 必須為 public

‣ 回傳 array 或 iterator

- ⾃自訂 iterator,CSV, XML…

Data Providers

那測試 3 該如何改成 data provider ?

Depends 與 Data Provider

被依賴的測試不能使⽤用 @dataProvider

$ 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

PHPUnit 提供的功能

‣ Assertions

‣ Test Dependencies

‣ Data Providers

‣ Test Exceptions

‣ Fixtures

測試 4 防⽌止重複報名

預期結果

1.重複報名者,則拋出異常

測試 4 防⽌止重複報名

預期結果

1.重複報名者,則拋出異常

為什麼異常也要測試?

?

Test Exceptions

‣ 預期測試⽅方法內拋出異常 ‣ 利⽤用 3 個標註:

- @expectedException ExceptionClass

- @expectedExceptionMessage ExceptionMessage

- @expectedExceptionCode ExceptionCode

PHPUnit 提供的功能

‣ Assertions

‣ Test Dependencies

‣ Data Providers

‣ Test Exceptions

‣ Fixtures

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/

Unit testing 的侷限

不會發現在系統層級的 錯誤或效能上的問題

還能忍受得了嗎 崩╰(〒⽫皿〒)╯潰組織與設定 PHPUnit

組織及設定 PHPUnit

‣ 設定 PHPUnit 測試

- phpunit ⼯工具執⾏行測試設定選項

- phpunit.xml 撰寫 XML 設定檔

組織及設定 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

Code Coverage

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

Q.A.

本著作除有特別註明外,係採⽤用創⽤用 CC 姓名標⽰示-相同⽅方式分享 4.0 國際 授權條款授權