169
PHPUnit 入門 Web 程式更加可靠 JACE JU 哇寶資訊技術總監

PHPUnit 入門介紹

  • Upload
    jace-ju

  • View
    14.453

  • Download
    1

Embed Size (px)

DESCRIPTION

介紹 PHPUnit 的基本概念

Citation preview

Page 1: PHPUnit 入門介紹

PHPUnit 入門

讓 Web 程式更加可靠

JACE JU

哇寶資訊技術總監

Page 2: PHPUnit 入門介紹

軟體測試

Page 3: PHPUnit 入門介紹

軟體測試

單元測試、功能測試、整合測試、

系統測試、壓力測試、使用者測

試…註:google://軟體測試

Page 4: PHPUnit 入門介紹

單元測試是什麼?

Page 5: PHPUnit 入門介紹

單元測試是什麼?

單元:程式中類別的方法或是函式

Page 6: PHPUnit 入門介紹

單元測試是什麼?

單元:程式中類別的方法或是函式

測試:驗證程式執行後預期的結果

Page 7: PHPUnit 入門介紹

單元測試的優點

Page 8: PHPUnit 入門介紹

為重構提供了保障

Page 9: PHPUnit 入門介紹

為重構提供了保障

在我們新增或修改程式時,測試

能確保舊的功能不受影響

Page 10: PHPUnit 入門介紹

讓開發者思考設計的範疇

Page 11: PHPUnit 入門介紹

讓開發者思考設計的範疇

在測試驅動開發 (TDD) 模式下 ,

測試其實就是設計

Page 12: PHPUnit 入門介紹

自動測試

Page 13: PHPUnit 入門介紹

自動測試

不需人工來一項一項地測試,讓

測試工具一次幫我們完成

Page 14: PHPUnit 入門介紹

單元測試的缺點?

Page 15: PHPUnit 入門介紹

測試太花時間?

Page 16: PHPUnit 入門介紹

測試太花時間?

測試寫的少, Debug 到老~

利用測試來節省尋找 Bug 的時間

Page 17: PHPUnit 入門介紹

沒有程式?怎麼測試?

Page 18: PHPUnit 入門介紹

沒有程式?怎麼測試?

測試就是讓我們確認程式應該要

表現出來的行為,所以反過來從

結果去推演出程式會更容易

Page 19: PHPUnit 入門介紹

Test-Driven Development測試驅動開發

Page 20: PHPUnit 入門介紹

測試先行

Page 21: PHPUnit 入門介紹

測試先行

將客戶的需求都轉換成測試,通

過測試就是完成需求

Page 22: PHPUnit 入門介紹

讓程式開發的方向有所依據

Page 23: PHPUnit 入門介紹

讓程式開發的方向有所依據

測試先行會讓程式開發方向收歛

在一定的合理範圍中

Page 24: PHPUnit 入門介紹

PHP 上的單元測試工具

Page 25: PHPUnit 入門介紹

SimpleTest

http://www.simpletest.org/

最大特色: PHP 4/5 相容

Page 26: PHPUnit 入門介紹

PHPUnit

http://www.phpunit.de/

最大特色:完整利用 PHP5 特色

(例如 SPL, Reflection…)註:SPL: Standard PHP Library

Page 27: PHPUnit 入門介紹

安裝 PHPUnit

Page 28: PHPUnit 入門介紹

安裝 PHPUnit

•從 PEAR 安裝

Page 29: PHPUnit 入門介紹

安裝 PHPUnit

•從 PEAR 安裝

•手動安裝

Page 30: PHPUnit 入門介紹

範例

第一個 PHPUnit 測試案例

Page 31: PHPUnit 入門介紹

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 . '.';

}}

Page 32: PHPUnit 入門介紹

PersonTest.php

<?php

require_once 'PHPUnit/Framework/TestCase.php';

Page 33: PHPUnit 入門介紹

PersonTest.php

<?php

require_once 'PHPUnit/Framework/TestCase.php';require_once 'Person.php';

Page 34: PHPUnit 入門介紹

PersonTest.php

<?php

require_once 'PHPUnit/Framework/TestCase.php';require_once 'Person.php';

class PersonTest extends PHPUnit_Framework_TestCase{

}

Page 35: PHPUnit 入門介紹

PersonTest.php

<?php

require_once 'PHPUnit/Framework/TestCase.php';require_once 'Person.php';

class PersonTest extends PHPUnit_Framework_TestCase{

public function testSayHelloTo(){

}}

Page 36: PHPUnit 入門介紹

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');

}}

Page 37: PHPUnit 入門介紹

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(

);

}}

Page 38: PHPUnit 入門介紹

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.',

);

}}

Page 39: PHPUnit 入門介紹

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')

);

}}

Page 40: PHPUnit 入門介紹

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

Page 41: PHPUnit 入門介紹

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)

Page 42: PHPUnit 入門介紹

怎麼樣算是好測試

Page 43: PHPUnit 入門介紹

說明開發者要表達的意圖

Page 44: PHPUnit 入門介紹

說明開發者要表達的意圖

測試就像程式的說明文件,要能

清楚傳達出如何使用這支程式

Page 45: PHPUnit 入門介紹

分離程式,以便測試

Page 46: PHPUnit 入門介紹

分離程式,以便測試

切細程式的功能,將能夠單獨測

試的部份獨立出來

Page 47: PHPUnit 入門介紹

測試結果要跟預期一樣

Page 48: PHPUnit 入門介紹

測試結果要跟預期一樣

測試出預期結果是應該的,而且

不能只有一次正確,而且在修改

程式後都要是一直正確的 (除非功

能改變)

Page 49: PHPUnit 入門介紹

好測試要可被依賴

Page 50: PHPUnit 入門介紹

好測試要可被依賴

單元測試要保證程式的正確性,

那麼單元測試也一定要值得信任

Page 51: PHPUnit 入門介紹

PHPUnit 的幾大特色

Page 52: PHPUnit 入門介紹

Data Providers

Page 53: PHPUnit 入門介紹

Data Providers

不需要為不同的測試資料重複相

同的程式碼

Page 54: PHPUnit 入門介紹

範例

多筆資料同時測試

Page 55: PHPUnit 入門介紹

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;

}

}

Page 56: PHPUnit 入門介紹

AddTest.php

<?php

require_once 'PHPUnit/Framework/TestCase.php';require_once 'Add.php';

class AddTest extends PHPUnit_Framework_TestCase{

}

Page 57: PHPUnit 入門介紹

AddTest.php

<?php

require_once 'PHPUnit/Framework/TestCase.php';require_once 'Add.php';

class AddTest extends PHPUnit_Framework_TestCase{

public function testAdd( )

{

}

}

Page 58: PHPUnit 入門介紹

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());

}

}

Page 59: PHPUnit 入門介紹

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());

}

}

Page 60: PHPUnit 入門介紹

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());

}

}

Page 61: PHPUnit 入門介紹

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(){

}}

Page 62: PHPUnit 入門介紹

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)

); // 可以用迴圈或讀檔的方式去產生大量的測試資料}

}

Page 63: PHPUnit 入門介紹

# phpunit AddTest.php

Page 64: PHPUnit 入門介紹

# phpunit AddTest.phpPHPUnit 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>.

xxxxxxxx\AddTest.php:15

FAILURES!

Tests: 4, Assertions: 4, Failures: 1.

Page 65: PHPUnit 入門介紹

Expected Exception

Page 66: PHPUnit 入門介紹

Expected Exception

預期程式會出現語法不正確以外

的錯誤或異常

Page 67: PHPUnit 入門介紹

範例

預期會出現異常的測試

Page 68: PHPUnit 入門介紹

ExceptionTest.php

<?php

require_once 'PHPUnit/Framework.php';

Page 69: PHPUnit 入門介紹

ExceptionTest.php

<?php

require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase

{

public function testException(){

}

}

Page 70: PHPUnit 入門介紹

ExceptionTest.php

<?php

require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase

{/*** @expectedException InvalidArgumentException*/

public function testException(){

}

}

Page 71: PHPUnit 入門介紹

ExceptionTest.php

<?php

require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase

{/*** @expectedException InvalidArgumentException*/

public function testException(){

}

}

# phpunit ExceptionTest.php

Page 72: PHPUnit 入門介紹

ExceptionTest.php

<?php

require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase

{/*** @expectedException InvalidArgumentException*/

public function testException(){

}

}

# phpunit ExceptionTest.phpPHPUnit 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.

Page 73: PHPUnit 入門介紹

ExceptionTest.php

<?php

require_once 'PHPUnit/Framework.php';

class ExceptionTest extends PHPUnit_Framework_TestCase

{/*** @expectedException InvalidArgumentException*/

public function testException(){

}

}

Page 74: PHPUnit 入門介紹

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 來完成

}

}

Page 75: PHPUnit 入門介紹

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

Page 76: PHPUnit 入門介紹

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.phpPHPUnit 3.3.17 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)

Page 77: PHPUnit 入門介紹

為什麼要測試錯誤?

Page 78: PHPUnit 入門介紹

輸入的資料千千百百種

Page 79: PHPUnit 入門介紹

輸入的資料千千百百種

因為不能預期使用者會按照正常

方法來使用程式,所以測試各種

狀況就是必要的動作

Page 80: PHPUnit 入門介紹

沒錯反而是錯的?

Page 81: PHPUnit 入門介紹

沒錯反而是錯的?

當程式明顯不能接受某些輸入值

時,程式應該出現異常,而我們

要能確保這件事的發生

Page 82: PHPUnit 入門介紹

Fixtures

Page 83: PHPUnit 入門介紹

Fixtures

為每個測試案例準備同一組測試

物件,以省去在每個測試方法中

建立測試物件的成本

Page 84: PHPUnit 入門介紹

範例

共用測試物件的測試案例

Page 85: PHPUnit 入門介紹

FixtureTest.php

<?php

require_once 'PHPUnit/Framework.php';

class ArrayTest extends PHPUnit_Framework_TestCase

{protected $fixture;

}

Page 86: PHPUnit 入門介紹

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));

}

}

Page 87: PHPUnit 入門介紹

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));

}

}

Page 88: PHPUnit 入門介紹

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));

}

}

Page 89: PHPUnit 入門介紹

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);}

}

Page 90: PHPUnit 入門介紹

執行步驟:

Page 91: PHPUnit 入門介紹

執行步驟:

setUp()

Page 92: PHPUnit 入門介紹

執行步驟:

setUp()testNewArrayIsEmpty()

Page 93: PHPUnit 入門介紹

執行步驟:

setUp()testNewArrayIsEmpty()tearDown()

Page 94: PHPUnit 入門介紹

執行步驟:

setUp()testNewArrayIsEmpty()tearDown()

setUp()

Page 95: PHPUnit 入門介紹

執行步驟:

setUp()testNewArrayIsEmpty()tearDown()

setUp()testArrayContainsAnElement()

Page 96: PHPUnit 入門介紹

執行步驟:

setUp()testNewArrayIsEmpty()tearDown()

setUp()testArrayContainsAnElement()tearDown()

Page 97: PHPUnit 入門介紹

Organizing Tests

Page 98: PHPUnit 入門介紹

Organizing Tests

將同性質 (套件、專案) 的測試案

例組織起來一起測試,就不需要

一個一個案例測試了

Page 99: PHPUnit 入門介紹

範例 1

組織套件中多個的類別的測試

Page 100: PHPUnit 入門介紹

Package/Class1Test.php

<?php

require_once 'PHPUnit/Framework.php';

class Package_Class1Test extends PHPUnit_Framework_TestCase

{public function testOne() {}

public function testTwo() {}}

Page 101: PHPUnit 入門介紹

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() {}}

Page 102: PHPUnit 入門介紹

Package/AllTests.php

<?php

require_once 'PHPUnit/Framework.php';

class Package_AllTests

{

}

Page 103: PHPUnit 入門介紹

Package/AllTests.php

<?php

require_once 'PHPUnit/Framework.php';

class Package_AllTests

{

public static function suite(){

}}

Page 104: PHPUnit 入門介紹

Package/AllTests.php

<?php

require_once 'PHPUnit/Framework.php';

class Package_AllTests

{

public static function suite(){

$suite = new PHPUnit_Framework_TestSuite('Package');

return $suite;

}}

Page 105: PHPUnit 入門介紹

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;

}}

Page 106: PHPUnit 入門介紹

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;

}}

Page 107: PHPUnit 入門介紹

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

Page 108: PHPUnit 入門介紹

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.phpPHPUnit 3.3.17 by Sebastian Bergmann.

....

Time: 0 seconds

OK (4 tests, 0 assertions)

Page 109: PHPUnit 入門介紹

範例 2

組織專案中多個套件的測試

Page 110: PHPUnit 入門介紹

AllTests.php

<?php

require_once 'PHPUnit/Framework.php';

class AllTests{

public static function suite(){

}

}

Page 111: PHPUnit 入門介紹

AllTests.php

<?php

require_once 'PHPUnit/Framework.php';

class AllTests{

public static function suite(){

$suite = new PHPUnit_Framework_TestSuite('Project');

return $suite;}

}

Page 112: PHPUnit 入門介紹

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;

}

}

Page 113: PHPUnit 入門介紹

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

Page 114: PHPUnit 入門介紹

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.phpPHPUnit 3.3.17 by Sebastian Bergmann.

....

Time: 0 seconds

OK (4 tests, 0 assertions)

Page 115: PHPUnit 入門介紹

範例 3

套件中使用 setUp 及 tearDown

Page 116: PHPUnit 入門介紹

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__;}

}

Page 117: PHPUnit 入門介紹

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__;}

}

Page 118: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

Page 119: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUp

Page 120: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUpMyTest::testOne

Page 121: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUpMyTest::testOneMyTest::tearDown

Page 122: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUpMyTest::testOneMyTest::tearDown

MyTest::setUp

Page 123: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUpMyTest::testOneMyTest::tearDown

MyTest::setUpMyTest::testTwo

Page 124: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUpMyTest::testOneMyTest::tearDown

MyTest::setUpMyTest::testTwoMyTest::tearDown.

Page 125: PHPUnit 入門介紹

執行步驟:

MySuite::setUp

MyTest::setUpMyTest::testOneMyTest::tearDown

MyTest::setUpMyTest::testTwoMyTest::tearDown.

MySuite::tearDown

Page 126: PHPUnit 入門介紹

範例 4

在組織測試中共用測試物件

Page 127: PHPUnit 入門介紹

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;

}

}

Page 128: PHPUnit 入門介紹

MyTest.php

<?php

require_once 'PHPUnit/Framework.php';

class MyTest extends PHPUnit_Framework_TestCase

{public function testOne()

{

$this->assertEquals('something', $this->sharedFixture);}

}

Page 129: PHPUnit 入門介紹

PHPUnit 基本結構

Page 130: PHPUnit 入門介紹

Composite 模式

Page 131: PHPUnit 入門介紹

Doubles

Page 132: PHPUnit 入門介紹

Doubles

對未產生或建立過程複雜的物件

進行模擬以區隔它們對測試的影

響,也就是 Mock Object註:PHPUnit 的 Mock 仿照自 jMock 1

Page 133: PHPUnit 入門介紹

範例 1

指定 Mock Object 的方法會輸出的值

Page 134: PHPUnit 入門介紹

StubTest.php

<?php

require_once 'PHPUnit/Framework.php';

class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容{

public function doSomething()

{

}}

class StubTest extends PHPUnit_Framework_TestCase{

public function testStub()

{

}

}

Page 135: PHPUnit 入門介紹

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

}

}

Page 136: PHPUnit 入門介紹

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()) // 不論呼叫幾次

}}

Page 137: PHPUnit 入門介紹

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') // 指定方法

}

}

Page 138: PHPUnit 入門介紹

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')); // 指定回傳值

}

}

Page 139: PHPUnit 入門介紹

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());}

}

Page 140: PHPUnit 入門介紹

範例 2

預測 Mock Object 的方法執行的次數

Page 141: PHPUnit 入門介紹

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);

}}

}

Page 142: PHPUnit 入門介紹

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()

{

}

}

Page 143: PHPUnit 入門介紹

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'));

}

}

Page 144: PHPUnit 入門介紹

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();

}

}

Page 145: PHPUnit 入門介紹

為什麼要 Mock Object?

Page 146: PHPUnit 入門介紹

隔離未實作的類別

Page 147: PHPUnit 入門介紹

隔離未實作的類別

在團隊開發時,伙伴負責的類別

尚未完成,我們必須隔離掉該部

份對目前單元的影響

Page 148: PHPUnit 入門介紹

無法使用線上開發

Page 149: PHPUnit 入門介紹

無法使用線上開發

像是線上刷卡等需要跟外部連線

的環境等

Page 150: PHPUnit 入門介紹

Code Coverage Analysis

Page 151: PHPUnit 入門介紹

Code Coverage Analysis

透過 Xdebug 來讓 PHPUnit 找出

測試所涵蓋到的程式碼

Page 152: PHPUnit 入門介紹

範例

Page 153: PHPUnit 入門介紹

# phpunit --coverage-html ./report SubjectTest.php

Page 154: PHPUnit 入門介紹

# phpunit --coverage-html ./report SubjectTest.phpPHPUnit 3.3.17 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)

Generating code coverage report, this may take a moment.

Page 155: PHPUnit 入門介紹

# phpunit --coverage-html ./report SubjectTest.phpPHPUnit 3.3.17 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)

Generating code coverage report, this may take a moment.

Page 156: PHPUnit 入門介紹

為什麼要知道測試涵蓋範圍?

Page 157: PHPUnit 入門介紹

找出尚未測試的部份

Page 158: PHPUnit 入門介紹

找出尚未測試的部份

程式碼測試的涵蓋範圍越廣,程

式就更加可靠

Page 159: PHPUnit 入門介紹

掌握開發進度

Page 160: PHPUnit 入門介紹

掌握開發進度

看著涵蓋範圍百分比的增加….

會很有成就感

Page 161: PHPUnit 入門介紹

PHP 需要單元測試嗎?

Page 162: PHPUnit 入門介紹

小案子就放它一馬

Page 163: PHPUnit 入門介紹

小案子就放它一馬

對 Web 開發而言,一般的 CRUD

並沒有特別需要做測試,像是留

言板、活動頁等註:CRUD: create, read, update, delete

Page 164: PHPUnit 入門介紹

測試有複雜演算的程式

Page 165: PHPUnit 入門介紹

測試有複雜演算的程式

資料樣本很多,而且容易出現不

同結果的狀況之下,就需要測試

Page 166: PHPUnit 入門介紹

測試有複雜演算的程式

資料樣本很多,而且容易出現不

同結果的狀況之下,就需要測試

不然就會像 Dell 一樣!

Page 167: PHPUnit 入門介紹

更多進階資訊

Page 168: PHPUnit 入門介紹

更多進階資訊

•PHPUnit 官方手冊

(http://www.phpunit.de/manual/3.3/en/index.html)

•jUnit

(http://www.junit.org)

•jMock

(http://www.jmock.org/)

Page 169: PHPUnit 入門介紹

謝謝大家