SlideShare a Scribd company logo
1 of 68
Download to read offline
第⼀一次⽤用 PHPUnit
寫測試就上⼿手
本著作除有特別註明外,係採⽤用創⽤用 CC 姓名標⽰示-相同⽅方式分享 4.0 國際 授權條款授權
2014/12/24
⿈黃儀銘 ymhuang@citi.sinica.edu.tw
⼤大綱
‣ 介紹 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 PHPUnitEventDemoEventException(
'Duplicated reservation',
PHPUnitEventDemoEventException::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 PHPUnitEventDemoEventException
* @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 PHPUnitEventDemoUser($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 PHPUnitEventDemoEvent(
$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 PHPUnitEventDemoUser($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 PHPUnitEventDemoEvent(
$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/EventTest
PHPUnit 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 MyEventTests
PHPUnit 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 國際 授權條款授權

More Related Content

What's hot

The ruby way test
The ruby way testThe ruby way test
The ruby way testDeng Peng
 
GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结coderzh
 
PHPUnit slide formal
PHPUnit slide formalPHPUnit slide formal
PHPUnit slide formaljameslabs
 
Maintainable PHP Source Code
Maintainable PHP Source CodeMaintainable PHP Source Code
Maintainable PHP Source CodeBo-Yi Wu
 
jsp基础速成精华讲解
jsp基础速成精华讲解jsp基础速成精华讲解
jsp基础速成精华讲解wensheng wei
 
钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会Zhi Zhong
 
cppcheck源码分析
cppcheck源码分析cppcheck源码分析
cppcheck源码分析Wu Liang
 
JavaScript 80+ Programming and Optimization Skills
JavaScript 80+ Programming and Optimization SkillsJavaScript 80+ Programming and Optimization Skills
JavaScript 80+ Programming and Optimization SkillsHo Kim
 
Cppcheck分析
Cppcheck分析Cppcheck分析
Cppcheck分析Wu Liang
 
OpenEJB - 另一個選擇
OpenEJB - 另一個選擇OpenEJB - 另一個選擇
OpenEJB - 另一個選擇Justin Lin
 
Django
DjangoDjango
DjangoTao He
 
嵌入式測試驅動開發
嵌入式測試驅動開發嵌入式測試驅動開發
嵌入式測試驅動開發hugo lu
 
Java SE 7 技術手冊 - 課後練習解答
Java SE 7 技術手冊 - 課後練習解答Java SE 7 技術手冊 - 課後練習解答
Java SE 7 技術手冊 - 課後練習解答Justin Lin
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析Justin Lin
 
2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發AgileCommunity
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试lydiafly
 
Patterns in Zend Framework
Patterns in Zend FrameworkPatterns in Zend Framework
Patterns in Zend FrameworkJace Ju
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程Bobby Zhou
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识yiditushe
 
Phpconf 2011 introduction_to_codeigniter
Phpconf 2011 introduction_to_codeigniterPhpconf 2011 introduction_to_codeigniter
Phpconf 2011 introduction_to_codeigniterBo-Yi Wu
 

What's hot (20)

The ruby way test
The ruby way testThe ruby way test
The ruby way test
 
GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结
 
PHPUnit slide formal
PHPUnit slide formalPHPUnit slide formal
PHPUnit slide formal
 
Maintainable PHP Source Code
Maintainable PHP Source CodeMaintainable PHP Source Code
Maintainable PHP Source Code
 
jsp基础速成精华讲解
jsp基础速成精华讲解jsp基础速成精华讲解
jsp基础速成精华讲解
 
钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会
 
cppcheck源码分析
cppcheck源码分析cppcheck源码分析
cppcheck源码分析
 
JavaScript 80+ Programming and Optimization Skills
JavaScript 80+ Programming and Optimization SkillsJavaScript 80+ Programming and Optimization Skills
JavaScript 80+ Programming and Optimization Skills
 
Cppcheck分析
Cppcheck分析Cppcheck分析
Cppcheck分析
 
OpenEJB - 另一個選擇
OpenEJB - 另一個選擇OpenEJB - 另一個選擇
OpenEJB - 另一個選擇
 
Django
DjangoDjango
Django
 
嵌入式測試驅動開發
嵌入式測試驅動開發嵌入式測試驅動開發
嵌入式測試驅動開發
 
Java SE 7 技術手冊 - 課後練習解答
Java SE 7 技術手冊 - 課後練習解答Java SE 7 技術手冊 - 課後練習解答
Java SE 7 技術手冊 - 課後練習解答
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發2014/02: 嵌入式測試驅動開發
2014/02: 嵌入式測試驅動開發
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试
 
Patterns in Zend Framework
Patterns in Zend FrameworkPatterns in Zend Framework
Patterns in Zend Framework
 
JavaScript 教程
JavaScript 教程JavaScript 教程
JavaScript 教程
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识
 
Phpconf 2011 introduction_to_codeigniter
Phpconf 2011 introduction_to_codeigniterPhpconf 2011 introduction_to_codeigniter
Phpconf 2011 introduction_to_codeigniter
 

Similar to 第一次用 PHPUnit 寫測試就上手

使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)Max Lai
 
广告技术部自动化测试介绍.pdf
广告技术部自动化测试介绍.pdf广告技术部自动化测试介绍.pdf
广告技术部自动化测试介绍.pdfbj_qa
 
Qa engineer training
Qa engineer trainingQa engineer training
Qa engineer trainingychw365
 
Codeception test
Codeception testCodeception test
Codeception testChin-Yu Ku
 
Foundation of software development 1
Foundation of software development 1Foundation of software development 1
Foundation of software development 1netdbncku
 
同济优秀课程设计 - 软件测试报告
同济优秀课程设计 - 软件测试报告同济优秀课程设计 - 软件测试报告
同济优秀课程设计 - 软件测试报告Kerry Zhu
 
軟體系統測試簡介
軟體系統測試簡介軟體系統測試簡介
軟體系統測試簡介Wei-Tsung Su
 
分布式系统测试实践
分布式系统测试实践分布式系统测试实践
分布式系统测试实践drewz lin
 
議題三:政府網站常見弱點與分析
議題三:政府網站常見弱點與分析議題三:政府網站常見弱點與分析
議題三:政府網站常見弱點與分析Nicolas su
 
作業系統數位教材(劉政雄)(1 9)
作業系統數位教材(劉政雄)(1 9)作業系統數位教材(劉政雄)(1 9)
作業系統數位教材(劉政雄)(1 9)Ying wei (Joe) Chou
 
Part04 软件测试方法论
Part04 软件测试方法论Part04 软件测试方法论
Part04 软件测试方法论aellaw
 
Foundation of software development 2
Foundation of software development 2Foundation of software development 2
Foundation of software development 2netdbncku
 
Continuous integration
Continuous integrationContinuous integration
Continuous integrationnetdbncku
 
Testing survey
Testing surveyTesting survey
Testing surveyTao He
 
Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013
Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013
Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013Hazem Saleh
 
2011性能测试技术
2011性能测试技术2011性能测试技术
2011性能测试技术070316
 

Similar to 第一次用 PHPUnit 寫測試就上手 (20)

使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)使用 Pytest 進行單元測試 (PyCon TW 2021)
使用 Pytest 進行單元測試 (PyCon TW 2021)
 
广告技术部自动化测试介绍.pdf
广告技术部自动化测试介绍.pdf广告技术部自动化测试介绍.pdf
广告技术部自动化测试介绍.pdf
 
Qa engineer training
Qa engineer trainingQa engineer training
Qa engineer training
 
PHP 单元测试
PHP 单元测试PHP 单元测试
PHP 单元测试
 
Codeception test
Codeception testCodeception test
Codeception test
 
Foundation of software development 1
Foundation of software development 1Foundation of software development 1
Foundation of software development 1
 
同济优秀课程设计 - 软件测试报告
同济优秀课程设计 - 软件测试报告同济优秀课程设计 - 软件测试报告
同济优秀课程设计 - 软件测试报告
 
軟體系統測試簡介
軟體系統測試簡介軟體系統測試簡介
軟體系統測試簡介
 
分布式系统测试实践
分布式系统测试实践分布式系统测试实践
分布式系统测试实践
 
所有Windows store 市集相關faq
所有Windows store 市集相關faq所有Windows store 市集相關faq
所有Windows store 市集相關faq
 
議題三:政府網站常見弱點與分析
議題三:政府網站常見弱點與分析議題三:政府網站常見弱點與分析
議題三:政府網站常見弱點與分析
 
作業系統數位教材(劉政雄)(1 9)
作業系統數位教材(劉政雄)(1 9)作業系統數位教材(劉政雄)(1 9)
作業系統數位教材(劉政雄)(1 9)
 
Part04 软件测试方法论
Part04 软件测试方法论Part04 软件测试方法论
Part04 软件测试方法论
 
Xpp
XppXpp
Xpp
 
8D process
8D process8D process
8D process
 
Foundation of software development 2
Foundation of software development 2Foundation of software development 2
Foundation of software development 2
 
Continuous integration
Continuous integrationContinuous integration
Continuous integration
 
Testing survey
Testing surveyTesting survey
Testing survey
 
Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013
Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013
Efficient JavaScript Unit Testing (Chinese Version), JavaOne China 2013
 
2011性能测试技术
2011性能测试技术2011性能测试技术
2011性能测试技术
 

第一次用 PHPUnit 寫測試就上手