PHPUnit 入門介紹
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

PHPUnit 入門介紹

on

  • 13,648 views

介紹 PHPUnit 的基本概念

介紹 PHPUnit 的基本概念

Statistics

Views

Total Views
13,648
Views on SlideShare
13,410
Embed Views
238

Actions

Likes
40
Downloads
288
Comments
0

6 Embeds 238

http://blog.wabow.com 187
http://www.plurk.com 17
http://www.slideshare.net 14
http://siuhungfa.net 13
http://gangpie.com 6
http://static.slidesharecdn.com 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

PHPUnit 入門介紹 Presentation Transcript

  • 1. PHPUnit 入門 讓 Web 程式更加可靠 JACE JU 哇寶資訊技術總監
  • 2. 軟體測試
  • 3. 軟體測試 單元測試、功能測試、整合測試、 系統測試、壓力測試、使用者測 試… 註:google://軟體測試
  • 4. 單元測試是什麼?
  • 5. 單元測試是什麼? 單元:程式中類別的方法或是函式
  • 6. 單元測試是什麼? 單元:程式中類別的方法或是函式 測試:驗證程式執行後預期的結果
  • 7. 單元測試的優點
  • 8. 為重構提供了保障
  • 9. 為重構提供了保障 在我們新增或修改程式時,測試 能確保舊的功能不受影響
  • 10. 讓開發者思考設計的範疇
  • 11. 讓開發者思考設計的範疇 在測試驅動開發 (TDD) 模式下 , 測試其實就是設計
  • 12. 自動測試
  • 13. 自動測試 不需人工來一項一項地測試,讓 測試工具一次幫我們完成
  • 14. 單元測試的缺點?
  • 15. 測試太花時間?
  • 16. 測試太花時間? 測試寫的少, Debug 到老~ 利用測試來節省尋找 Bug 的時間
  • 17. 沒有程式?怎麼測試?
  • 18. 沒有程式?怎麼測試? 測試就是讓我們確認程式應該要 表現出來的行為,所以反過來從 結果去推演出程式會更容易
  • 19. Test-Driven Development 測試驅動開發
  • 20. 測試先行
  • 21. 測試先行 將客戶的需求都轉換成測試,通 過測試就是完成需求
  • 22. 讓程式開發的方向有所依據
  • 23. 讓程式開發的方向有所依據 測試先行會讓程式開發方向收歛 在一定的合理範圍中
  • 24. PHP 上的單元測試工具
  • 25. SimpleTest http://www.simpletest.org/ 最大特色: PHP 4/5 相容
  • 26. PHPUnit http://www.phpunit.de/ 最大特色:完整利用 PHP5 特色 (例如 SPL, Reflection…) 註:SPL: Standard PHP Library
  • 27. 安裝 PHPUnit
  • 28. 安裝 PHPUnit •從 PEAR 安裝
  • 29. 安裝 PHPUnit •從 PEAR 安裝 •手動安裝
  • 30. 範例 第一個 PHPUnit 測試案例
  • 31. Person.php <?php class Person { protected $_name = ''; public function __construct($name) { $this->_name = (string) $name; } public function sayHelloTo($name) { return 'Hello, ' . $name . '. I am ' . $this->_name . '.'; } }
  • 32. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php';
  • 33. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php';
  • 34. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { }
  • 35. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { } }
  • 36. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { $person = new Person('Jace'); } }
  • 37. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { $person = new Person('Jace'); $this->assertEquals( ); } }
  • 38. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { $person = new Person('Jace'); $this->assertEquals( 'Hello, Neo. I am Jace.', ); } }
  • 39. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { $person = new Person('Jace'); $this->assertEquals( 'Hello, Neo. I am Jace.', $person->sayHelloTo('Neo') ); } }
  • 40. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { $person = new Person('Jace'); $this->assertEquals( 'Hello, Neo. I am Jace.', $person->sayHelloTo('Neo') ); } } # phpunit PersonTest.php
  • 41. PersonTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Person.php'; class PersonTest extends PHPUnit_Framework_TestCase { public function testSayHelloTo() { $person = new Person('Jace'); $this->assertEquals( 'Hello, Neo. I am Jace.', $person->sayHelloTo('Neo') ); } } # phpunit PersonTest.php PHPUnit 3.3.17 by Sebastian Bergmann. . Time: 0 seconds OK (1 test, 1 assertion)
  • 42. 怎麼樣算是好測試
  • 43. 說明開發者要表達的意圖
  • 44. 說明開發者要表達的意圖 測試就像程式的說明文件,要能 清楚傳達出如何使用這支程式
  • 45. 分離程式,以便測試
  • 46. 分離程式,以便測試 切細程式的功能,將能夠單獨測 試的部份獨立出來
  • 47. 測試結果要跟預期一樣
  • 48. 測試結果要跟預期一樣 測試出預期結果是應該的,而且 不能只有一次正確,而且在修改 程式後都要是一直正確的 (除非功 能改變)
  • 49. 好測試要可被依賴
  • 50. 好測試要可被依賴 單元測試要保證程式的正確性, 那麼單元測試也一定要值得信任
  • 51. PHPUnit 的幾大特色
  • 52. Data Providers
  • 53. Data Providers 不需要為不同的測試資料重複相 同的程式碼
  • 54. 範例 多筆資料同時測試
  • 55. Add.php <?php class Add { protected $_num1 = 0, $_num2 = 0; public function setNum1($num) { $this->_num1 = (int) $num; } public function setNum2($num) { $this->_num2 = (int) $num; } public function getResult() { return $this->_num1 + $this->_num2; } }
  • 56. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { }
  • 57. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { public function testAdd( ) { } }
  • 58. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { public function testAdd( ) { $add = new Add(); $add->setNum1($num1); $add->setNum2($num2); $this->assertEquals($expection, $add->getResult()); } }
  • 59. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { public function testAdd($num1, $num2, $expection) { $add = new Add(); $add->setNum1($num1); $add->setNum2($num2); $this->assertEquals($expection, $add->getResult()); } }
  • 60. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provider */ public function testAdd($num1, $num2, $expection) { $add = new Add(); $add->setNum1($num1); $add->setNum2($num2); $this->assertEquals($expection, $add->getResult()); } }
  • 61. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provider */ public function testAdd($num1, $num2, $expection) { $add = new Add(); $add->setNum1($num1); $add->setNum2($num2); $this->assertEquals($expection, $add->getResult()); } public function provider() { } }
  • 62. AddTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; require_once 'Add.php'; class AddTest extends PHPUnit_Framework_TestCase { /** * @dataProvider provider */ public function testAdd($num1, $num2, $expection) { $add = new Add(); $add->setNum1($num1); $add->setNum2($num2); $this->assertEquals($expection, $add->getResult()); } public function provider() { return array( array(0, 0, 0), array(0, 1, 1), array(1, 0, 1), array(1, 1, 3) ); // 可以用迴圈或讀檔的方式去產生大量的測試資料 } }
  • 63. # phpunit AddTest.php
  • 64. # phpunit AddTest.php PHPUnit 3.3.17 by Sebastian Bergmann. ...F Time: 0 seconds There was 1 failure: 1) testAdd(AddTest) with data set #3 (1, 1, 3) Failed asserting that <integer:2> matches expected value <integer:3>. xxxxxxxxAddTest.php:15 FAILURES! Tests: 4, Assertions: 4, Failures: 1.
  • 65. Expected Exception
  • 66. Expected Exception 預期程式會出現語法不正確以外 的錯誤或異常
  • 67. 範例 預期會出現異常的測試
  • 68. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php';
  • 69. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { public function testException() { } }
  • 70. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { } }
  • 71. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { } } # phpunit ExceptionTest.php
  • 72. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { } } # phpunit ExceptionTest.php PHPUnit 3.3.17 by Sebastian Bergmann. F Time: 0 seconds There was 1 failure: 1) testException(ExceptionTest) Expected exception InvalidArgumentException FAILURES! Tests: 1, Assertions: 1, Failures: 1.
  • 73. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { } }
  • 74. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { throw new InvalidArgumentException('Test'); // 實作上應用 try … catch 來完成 } }
  • 75. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { throw new InvalidArgumentException('Test'); // 實作上應用 try … catch 來完成 } } # phpunit ExceptionTest.php
  • 76. ExceptionTest.php <?php require_once 'PHPUnit/Framework.php'; class ExceptionTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testException() { throw new InvalidArgumentException('Test'); // 實作上應用 try … catch 來完成 } } # phpunit ExceptionTest.php PHPUnit 3.3.17 by Sebastian Bergmann. . Time: 0 seconds OK (1 test, 1 assertion)
  • 77. 為什麼要測試錯誤?
  • 78. 輸入的資料千千百百種
  • 79. 輸入的資料千千百百種 因為不能預期使用者會按照正常 方法來使用程式,所以測試各種 狀況就是必要的動作
  • 80. 沒錯反而是錯的?
  • 81. 沒錯反而是錯的? 當程式明顯不能接受某些輸入值 時,程式應該出現異常,而我們 要能確保這件事的發生
  • 82. Fixtures
  • 83. Fixtures 為每個測試案例準備同一組測試 物件,以省去在每個測試方法中 建立測試物件的成本
  • 84. 範例 共用測試物件的測試案例
  • 85. FixtureTest.php <?php require_once 'PHPUnit/Framework.php'; class ArrayTest extends PHPUnit_Framework_TestCase { protected $fixture; }
  • 86. FixtureTest.php <?php require_once 'PHPUnit/Framework.php'; class ArrayTest extends PHPUnit_Framework_TestCase { protected $fixture; public function testNewArrayIsEmpty() { $this->assertEquals(0, sizeof($this->fixture)); } }
  • 87. FixtureTest.php <?php require_once 'PHPUnit/Framework.php'; class ArrayTest extends PHPUnit_Framework_TestCase { protected $fixture; public function testNewArrayIsEmpty() { $this->assertEquals(0, sizeof($this->fixture)); } public function testArrayContainsAnElement() { $this->fixture[] = 'Element'; $this->assertEquals(1, sizeof($this->fixture)); } }
  • 88. FixtureTest.php <?php require_once 'PHPUnit/Framework.php'; class ArrayTest extends PHPUnit_Framework_TestCase { protected $fixture; protected function setUp() { $this->fixture = array(); } public function testNewArrayIsEmpty() { $this->assertEquals(0, sizeof($this->fixture)); } public function testArrayContainsAnElement() { $this->fixture[] = 'Element'; $this->assertEquals(1, sizeof($this->fixture)); } }
  • 89. FixtureTest.php <?php require_once 'PHPUnit/Framework.php'; class ArrayTest extends PHPUnit_Framework_TestCase { protected $fixture; protected function setUp() { $this->fixture = array(); } public function testNewArrayIsEmpty() { $this->assertEquals(0, sizeof($this->fixture)); } public function testArrayContainsAnElement() { $this->fixture[] = 'Element'; $this->assertEquals(1, sizeof($this->fixture)); } protected function tearDown() { unset($this->fixture); } }
  • 90. 執行步驟:
  • 91. 執行步驟: setUp()
  • 92. 執行步驟: setUp() testNewArrayIsEmpty()
  • 93. 執行步驟: setUp() testNewArrayIsEmpty() tearDown()
  • 94. 執行步驟: setUp() testNewArrayIsEmpty() tearDown() setUp()
  • 95. 執行步驟: setUp() testNewArrayIsEmpty() tearDown() setUp() testArrayContainsAnElement()
  • 96. 執行步驟: setUp() testNewArrayIsEmpty() tearDown() setUp() testArrayContainsAnElement() tearDown()
  • 97. Organizing Tests
  • 98. Organizing Tests 將同性質 (套件、專案) 的測試案 例組織起來一起測試,就不需要 一個一個案例測試了
  • 99. 範例 1 組織套件中多個的類別的測試
  • 100. Package/Class1Test.php <?php require_once 'PHPUnit/Framework.php'; class Package_Class1Test extends PHPUnit_Framework_TestCase { public function testOne() {} public function testTwo() {} }
  • 101. Package/Class1Test.php <?php require_once 'PHPUnit/Framework.php'; class Package_Class1Test extends PHPUnit_Framework_TestCase { public function testOne() {} public function testTwo() {} } Package/Class2Test.php <?php require_once 'PHPUnit/Framework.php'; class Package_Class2Test extends PHPUnit_Framework_TestCase { public function testThree() {} public function testFour() {} }
  • 102. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; class Package_AllTests { }
  • 103. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; class Package_AllTests { public static function suite() { } }
  • 104. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; class Package_AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Package'); return $suite; } }
  • 105. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/Class1Test.php'; class Package_AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Package'); $suite->addTestSuite('Package_Class1Test'); return $suite; } }
  • 106. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/Class1Test.php'; require_once 'Package/Class2Test.php'; class Package_AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Package'); $suite->addTestSuite('Package_Class1Test'); $suite->addTestSuite('Package_Class2Test'); return $suite; } }
  • 107. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/Class1Test.php'; require_once 'Package/Class2Test.php'; class Package_AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Package'); $suite->addTestSuite('Package_Class1Test'); $suite->addTestSuite('Package_Class2Test'); return $suite; } } # phpunit Package/AllTests.php
  • 108. Package/AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/Class1Test.php'; require_once 'Package/Class2Test.php'; class Package_AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Package'); $suite->addTestSuite('Package_Class1Test'); $suite->addTestSuite('Package_Class2Test'); return $suite; } } # phpunit Package/AllTests.php PHPUnit 3.3.17 by Sebastian Bergmann. .... Time: 0 seconds OK (4 tests, 0 assertions)
  • 109. 範例 2 組織專案中多個套件的測試
  • 110. AllTests.php <?php require_once 'PHPUnit/Framework.php'; class AllTests { public static function suite() { } }
  • 111. AllTests.php <?php require_once 'PHPUnit/Framework.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Project'); return $suite; } }
  • 112. AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/AllTests.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Project'); $suite->addTest(Package_AllTests::suite()); return $suite; } }
  • 113. AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/AllTests.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Project'); $suite->addTest(Package_AllTests::suite()); return $suite; } } # phpunit AllTests.php
  • 114. AllTests.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Package/AllTests.php'; class AllTests { public static function suite() { $suite = new PHPUnit_Framework_TestSuite('Project'); $suite->addTest(Package_AllTests::suite()); return $suite; } } # phpunit AllTests.php PHPUnit 3.3.17 by Sebastian Bergmann. .... Time: 0 seconds OK (4 tests, 0 assertions)
  • 115. 範例 3 套件中使用 setUp 及 tearDown
  • 116. MyTest.php <?php require_once 'PHPUnit/Framework.php'; class MyTest extends PHPUnit_Framework_TestCase { protected function setUp() { echo "n", __METHOD__; } public function testOne() { echo "n", __METHOD__; } public function testTwo() { echo "n", __METHOD__; } public function tearDown() { echo "n", __METHOD__; } }
  • 117. MySuite.php <?php require_once 'MyTest.php'; class MySuite extends PHPUnit_Framework_TestSuite { public static function suite() { return new self('MyTest'); } protected function setUp() { echo "n", __METHOD__; } protected function tearDown() { echo "n", __METHOD__; } }
  • 118. 執行步驟: MySuite::setUp
  • 119. 執行步驟: MySuite::setUp MyTest::setUp
  • 120. 執行步驟: MySuite::setUp MyTest::setUp MyTest::testOne
  • 121. 執行步驟: MySuite::setUp MyTest::setUp MyTest::testOne MyTest::tearDown
  • 122. 執行步驟: MySuite::setUp MyTest::setUp MyTest::testOne MyTest::tearDown MyTest::setUp
  • 123. 執行步驟: MySuite::setUp MyTest::setUp MyTest::testOne MyTest::tearDown MyTest::setUp MyTest::testTwo
  • 124. 執行步驟: MySuite::setUp MyTest::setUp MyTest::testOne MyTest::tearDown MyTest::setUp MyTest::testTwo MyTest::tearDown.
  • 125. 執行步驟: MySuite::setUp MyTest::setUp MyTest::testOne MyTest::tearDown MyTest::setUp MyTest::testTwo MyTest::tearDown. MySuite::tearDown
  • 126. 範例 4 在組織測試中共用測試物件
  • 127. MySuite.php <?php require_once 'MyTest.php'; class MySuite extends PHPUnit_Framework_TestSuite { public static function suite() { return new self('MyTest'); } protected function setUp() { $this->sharedFixture = 'something'; } protected function tearDown() { $this->sharedFixture = null; } }
  • 128. MyTest.php <?php require_once 'PHPUnit/Framework.php'; class MyTest extends PHPUnit_Framework_TestCase { public function testOne() { $this->assertEquals('something', $this->sharedFixture); } }
  • 129. PHPUnit 基本結構
  • 130. Composite 模式
  • 131. Doubles
  • 132. Doubles 對未產生或建立過程複雜的物件 進行模擬以區隔它們對測試的影 響,也就是 Mock Object 註:PHPUnit 的 Mock 仿照自 jMock 1
  • 133. 範例 1 指定 Mock Object 的方法會輸出的值
  • 134. StubTest.php <?php require_once 'PHPUnit/Framework.php'; class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容 { public function doSomething() { } } class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { } }
  • 135. StubTest.php <?php require_once 'PHPUnit/Framework.php'; class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容 { public function doSomething() { } } class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); // 建立 Mock Object } }
  • 136. StubTest.php <?php require_once 'PHPUnit/Framework.php'; class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容 { public function doSomething() { } } class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); // 建立 Mock Object $stub->expects($this->any()) // 不論呼叫幾次 } }
  • 137. StubTest.php <?php require_once 'PHPUnit/Framework.php'; class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容 { public function doSomething() { } } class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); // 建立 Mock Object $stub->expects($this->any()) // 不論呼叫幾次 ->method('doSomething') // 指定方法 } }
  • 138. StubTest.php <?php require_once 'PHPUnit/Framework.php'; class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容 { public function doSomething() { } } class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); // 建立 Mock Object $stub->expects($this->any()) // 不論呼叫幾次 ->method('doSomething') // 指定方法 ->will($this->returnValue('foo')); // 指定回傳值 } }
  • 139. StubTest.php <?php require_once 'PHPUnit/Framework.php'; class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容 { public function doSomething() { } } class StubTest extends PHPUnit_Framework_TestCase { public function testStub() { $stub = $this->getMock('SomeClass'); // 建立 Mock Object $stub->expects($this->any()) // 不論呼叫幾次 ->method('doSomething') // 指定方法 ->will($this->returnValue('foo')); // 指定回傳值 $this->assertEquals('foo', $stub->doSomething()); } }
  • 140. 範例 2 預測 Mock Object 的方法執行的次數
  • 141. Subject.php <?php class Subject { protected $observers = array(); public function attach(Observer $observer) { $this->observers[] = $observer; } public function doSomething() { $this->notify('something'); } protected function notify($arg) { foreach ($this->observers as $observer) { $observer->update($arg); } } }
  • 142. SubjectTest.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Subject.php'; class Observer // 雖然實際的 Observer 未完成,但我們還是要給它一個類別宣告 { public function update($arg) {} } class SubjectTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { } }
  • 143. SubjectTest.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Subject.php'; class Observer // 雖然實際的 Observer 未完成,但我們還是要給它一個類別宣告 { public function update($arg) {} } class SubjectTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { // 建立一個 Observer 的 Mock Object $observer = $this->getMock('Observer'); // 預期 Observer::update 方法應該只跑一次 // 而傳入 update 方法的參數值為 something $observer->expects($this->once()) ->method('update') ->with($this->equalTo('something')); } }
  • 144. SubjectTest.php <?php require_once 'PHPUnit/Framework.php'; require_once 'Subject.php'; class Observer // 雖然實際的 Observer 未完成,但我們還是要給它一個類別宣告 { public function update($arg) {} } class SubjectTest extends PHPUnit_Framework_TestCase { public function testUpdateIsCalledOnce() { // 建立一個 Observer 的 Mock Object $observer = $this->getMock('Observer'); // 預期 Observer::update 方法應該只跑一次 // 而傳入 update 方法的參數值為 something $observer->expects($this->once()) ->method('update') ->with($this->equalTo('something')); $subject = new Subject(); $subject->attach($observer); // 我們預測這裡會呼叫 Observer::update() 一次 $subject->doSomething(); } }
  • 145. 為什麼要 Mock Object?
  • 146. 隔離未實作的類別
  • 147. 隔離未實作的類別 在團隊開發時,伙伴負責的類別 尚未完成,我們必須隔離掉該部 份對目前單元的影響
  • 148. 無法使用線上開發
  • 149. 無法使用線上開發 像是線上刷卡等需要跟外部連線 的環境等
  • 150. Code Coverage Analysis
  • 151. Code Coverage Analysis 透過 Xdebug 來讓 PHPUnit 找出 測試所涵蓋到的程式碼
  • 152. 範例
  • 153. # phpunit --coverage-html ./report SubjectTest.php
  • 154. # phpunit --coverage-html ./report SubjectTest.php PHPUnit 3.3.17 by Sebastian Bergmann. . Time: 0 seconds OK (1 test, 1 assertion) Generating code coverage report, this may take a moment.
  • 155. # phpunit --coverage-html ./report SubjectTest.php PHPUnit 3.3.17 by Sebastian Bergmann. . Time: 0 seconds OK (1 test, 1 assertion) Generating code coverage report, this may take a moment.
  • 156. 為什麼要知道測試涵蓋範圍?
  • 157. 找出尚未測試的部份
  • 158. 找出尚未測試的部份 程式碼測試的涵蓋範圍越廣,程 式就更加可靠
  • 159. 掌握開發進度
  • 160. 掌握開發進度 看著涵蓋範圍百分比的增加…. 會很有成就感
  • 161. PHP 需要單元測試嗎?
  • 162. 小案子就放它一馬
  • 163. 小案子就放它一馬 對 Web 開發而言,一般的 CRUD 並沒有特別需要做測試,像是留 言板、活動頁等 註:CRUD: create, read, update, delete
  • 164. 測試有複雜演算的程式
  • 165. 測試有複雜演算的程式 資料樣本很多,而且容易出現不 同結果的狀況之下,就需要測試
  • 166. 測試有複雜演算的程式 資料樣本很多,而且容易出現不 同結果的狀況之下,就需要測試 不然就會像 Dell 一樣!
  • 167. 更多進階資訊
  • 168. 更多進階資訊 •PHPUnit 官方手冊 (http://www.phpunit.de/manual/3.3/en/index.html) •jUnit (http://www.junit.org) •jMock (http://www.jmock.org/)
  • 169. 謝謝大家