Upload
jace-ju
View
14.453
Download
1
Embed Size (px)
DESCRIPTION
介紹 PHPUnit 的基本概念
Citation preview
PHPUnit 入門
讓 Web 程式更加可靠
JACE JU
哇寶資訊技術總監
軟體測試
軟體測試
單元測試、功能測試、整合測試、
系統測試、壓力測試、使用者測
試…註:google://軟體測試
單元測試是什麼?
單元測試是什麼?
單元:程式中類別的方法或是函式
單元測試是什麼?
單元:程式中類別的方法或是函式
測試:驗證程式執行後預期的結果
單元測試的優點
為重構提供了保障
為重構提供了保障
在我們新增或修改程式時,測試
能確保舊的功能不受影響
讓開發者思考設計的範疇
讓開發者思考設計的範疇
在測試驅動開發 (TDD) 模式下 ,
測試其實就是設計
自動測試
自動測試
不需人工來一項一項地測試,讓
測試工具一次幫我們完成
單元測試的缺點?
測試太花時間?
測試太花時間?
測試寫的少, Debug 到老~
利用測試來節省尋找 Bug 的時間
沒有程式?怎麼測試?
沒有程式?怎麼測試?
測試就是讓我們確認程式應該要
表現出來的行為,所以反過來從
結果去推演出程式會更容易
Test-Driven Development測試驅動開發
測試先行
測試先行
將客戶的需求都轉換成測試,通
過測試就是完成需求
讓程式開發的方向有所依據
讓程式開發的方向有所依據
測試先行會讓程式開發方向收歛
在一定的合理範圍中
PHP 上的單元測試工具
SimpleTest
http://www.simpletest.org/
最大特色: PHP 4/5 相容
PHPUnit
http://www.phpunit.de/
最大特色:完整利用 PHP5 特色
(例如 SPL, Reflection…)註:SPL: Standard PHP Library
安裝 PHPUnit
安裝 PHPUnit
•從 PEAR 安裝
安裝 PHPUnit
•從 PEAR 安裝
•手動安裝
範例
第一個 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 . '.';
}}
PersonTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';
PersonTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';require_once 'Person.php';
PersonTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';require_once 'Person.php';
class PersonTest extends PHPUnit_Framework_TestCase{
}
PersonTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';require_once 'Person.php';
class PersonTest extends PHPUnit_Framework_TestCase{
public function testSayHelloTo(){
}}
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');
}}
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(
);
}}
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.',
);
}}
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')
);
}}
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
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)
怎麼樣算是好測試
說明開發者要表達的意圖
說明開發者要表達的意圖
測試就像程式的說明文件,要能
清楚傳達出如何使用這支程式
分離程式,以便測試
分離程式,以便測試
切細程式的功能,將能夠單獨測
試的部份獨立出來
測試結果要跟預期一樣
測試結果要跟預期一樣
測試出預期結果是應該的,而且
不能只有一次正確,而且在修改
程式後都要是一直正確的 (除非功
能改變)
好測試要可被依賴
好測試要可被依賴
單元測試要保證程式的正確性,
那麼單元測試也一定要值得信任
PHPUnit 的幾大特色
Data Providers
Data Providers
不需要為不同的測試資料重複相
同的程式碼
範例
多筆資料同時測試
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;
}
}
AddTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';require_once 'Add.php';
class AddTest extends PHPUnit_Framework_TestCase{
}
AddTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';require_once 'Add.php';
class AddTest extends PHPUnit_Framework_TestCase{
public function testAdd( )
{
}
}
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());
}
}
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());
}
}
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());
}
}
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(){
}}
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)
); // 可以用迴圈或讀檔的方式去產生大量的測試資料}
}
# phpunit AddTest.php
# 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.
Expected Exception
Expected Exception
預期程式會出現語法不正確以外
的錯誤或異常
範例
預期會出現異常的測試
ExceptionTest.php
<?php
require_once 'PHPUnit/Framework.php';
ExceptionTest.php
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase
{
public function testException(){
}
}
ExceptionTest.php
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase
{/*** @expectedException InvalidArgumentException*/
public function testException(){
}
}
ExceptionTest.php
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase
{/*** @expectedException InvalidArgumentException*/
public function testException(){
}
}
# phpunit ExceptionTest.php
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.
ExceptionTest.php
<?php
require_once 'PHPUnit/Framework.php';
class ExceptionTest extends PHPUnit_Framework_TestCase
{/*** @expectedException InvalidArgumentException*/
public function testException(){
}
}
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 來完成
}
}
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
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)
為什麼要測試錯誤?
輸入的資料千千百百種
輸入的資料千千百百種
因為不能預期使用者會按照正常
方法來使用程式,所以測試各種
狀況就是必要的動作
沒錯反而是錯的?
沒錯反而是錯的?
當程式明顯不能接受某些輸入值
時,程式應該出現異常,而我們
要能確保這件事的發生
Fixtures
Fixtures
為每個測試案例準備同一組測試
物件,以省去在每個測試方法中
建立測試物件的成本
範例
共用測試物件的測試案例
FixtureTest.php
<?php
require_once 'PHPUnit/Framework.php';
class ArrayTest extends PHPUnit_Framework_TestCase
{protected $fixture;
}
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));
}
}
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));
}
}
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));
}
}
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);}
}
執行步驟:
執行步驟:
setUp()
執行步驟:
setUp()testNewArrayIsEmpty()
執行步驟:
setUp()testNewArrayIsEmpty()tearDown()
執行步驟:
setUp()testNewArrayIsEmpty()tearDown()
setUp()
執行步驟:
setUp()testNewArrayIsEmpty()tearDown()
setUp()testArrayContainsAnElement()
執行步驟:
setUp()testNewArrayIsEmpty()tearDown()
setUp()testArrayContainsAnElement()tearDown()
Organizing Tests
Organizing Tests
將同性質 (套件、專案) 的測試案
例組織起來一起測試,就不需要
一個一個案例測試了
範例 1
組織套件中多個的類別的測試
Package/Class1Test.php
<?php
require_once 'PHPUnit/Framework.php';
class Package_Class1Test extends PHPUnit_Framework_TestCase
{public function testOne() {}
public function testTwo() {}}
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() {}}
Package/AllTests.php
<?php
require_once 'PHPUnit/Framework.php';
class Package_AllTests
{
}
Package/AllTests.php
<?php
require_once 'PHPUnit/Framework.php';
class Package_AllTests
{
public static function suite(){
}}
Package/AllTests.php
<?php
require_once 'PHPUnit/Framework.php';
class Package_AllTests
{
public static function suite(){
$suite = new PHPUnit_Framework_TestSuite('Package');
return $suite;
}}
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;
}}
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;
}}
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
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)
範例 2
組織專案中多個套件的測試
AllTests.php
<?php
require_once 'PHPUnit/Framework.php';
class AllTests{
public static function suite(){
}
}
AllTests.php
<?php
require_once 'PHPUnit/Framework.php';
class AllTests{
public static function suite(){
$suite = new PHPUnit_Framework_TestSuite('Project');
return $suite;}
}
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;
}
}
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
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)
範例 3
套件中使用 setUp 及 tearDown
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__;}
}
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__;}
}
執行步驟:
MySuite::setUp
執行步驟:
MySuite::setUp
MyTest::setUp
執行步驟:
MySuite::setUp
MyTest::setUpMyTest::testOne
執行步驟:
MySuite::setUp
MyTest::setUpMyTest::testOneMyTest::tearDown
執行步驟:
MySuite::setUp
MyTest::setUpMyTest::testOneMyTest::tearDown
MyTest::setUp
執行步驟:
MySuite::setUp
MyTest::setUpMyTest::testOneMyTest::tearDown
MyTest::setUpMyTest::testTwo
執行步驟:
MySuite::setUp
MyTest::setUpMyTest::testOneMyTest::tearDown
MyTest::setUpMyTest::testTwoMyTest::tearDown.
執行步驟:
MySuite::setUp
MyTest::setUpMyTest::testOneMyTest::tearDown
MyTest::setUpMyTest::testTwoMyTest::tearDown.
MySuite::tearDown
範例 4
在組織測試中共用測試物件
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;
}
}
MyTest.php
<?php
require_once 'PHPUnit/Framework.php';
class MyTest extends PHPUnit_Framework_TestCase
{public function testOne()
{
$this->assertEquals('something', $this->sharedFixture);}
}
PHPUnit 基本結構
Composite 模式
Doubles
Doubles
對未產生或建立過程複雜的物件
進行模擬以區隔它們對測試的影
響,也就是 Mock Object註:PHPUnit 的 Mock 仿照自 jMock 1
範例 1
指定 Mock Object 的方法會輸出的值
StubTest.php
<?php
require_once 'PHPUnit/Framework.php';
class SomeClass // 給 Mock Object 用的類別宣告,不一定要有實際內容{
public function doSomething()
{
}}
class StubTest extends PHPUnit_Framework_TestCase{
public function testStub()
{
}
}
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
}
}
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()) // 不論呼叫幾次
}}
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') // 指定方法
}
}
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')); // 指定回傳值
}
}
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());}
}
範例 2
預測 Mock Object 的方法執行的次數
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);
}}
}
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()
{
}
}
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'));
}
}
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();
}
}
為什麼要 Mock Object?
隔離未實作的類別
隔離未實作的類別
在團隊開發時,伙伴負責的類別
尚未完成,我們必須隔離掉該部
份對目前單元的影響
無法使用線上開發
無法使用線上開發
像是線上刷卡等需要跟外部連線
的環境等
Code Coverage Analysis
Code Coverage Analysis
透過 Xdebug 來讓 PHPUnit 找出
測試所涵蓋到的程式碼
範例
# phpunit --coverage-html ./report SubjectTest.php
# 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.
# 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.
為什麼要知道測試涵蓋範圍?
找出尚未測試的部份
找出尚未測試的部份
程式碼測試的涵蓋範圍越廣,程
式就更加可靠
掌握開發進度
掌握開發進度
看著涵蓋範圍百分比的增加….
會很有成就感
PHP 需要單元測試嗎?
小案子就放它一馬
小案子就放它一馬
對 Web 開發而言,一般的 CRUD
並沒有特別需要做測試,像是留
言板、活動頁等註:CRUD: create, read, update, delete
測試有複雜演算的程式
測試有複雜演算的程式
資料樣本很多,而且容易出現不
同結果的狀況之下,就需要測試
測試有複雜演算的程式
資料樣本很多,而且容易出現不
同結果的狀況之下,就需要測試
不然就會像 Dell 一樣!
更多進階資訊
更多進階資訊
•PHPUnit 官方手冊
(http://www.phpunit.de/manual/3.3/en/index.html)
•jUnit
(http://www.junit.org)
•jMock
(http://www.jmock.org/)
謝謝大家