SlideShare a Scribd company logo
1 of 169
Download to read offline
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.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.
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.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.
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.php
PHPUnit 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.php
PHPUnit 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.php
PHPUnit 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::setUp
MyTest::testOne
執行步驟:

MySuite::setUp

MyTest::setUp
MyTest::testOne
MyTest::tearDown
執行步驟:

MySuite::setUp

MyTest::setUp
MyTest::testOne
MyTest::tearDown

MyTest::setUp
執行步驟:

MySuite::setUp

MyTest::setUp
MyTest::testOne
MyTest::tearDown

MyTest::setUp
MyTest::testTwo
執行步驟:

MySuite::setUp

MyTest::setUp
MyTest::testOne
MyTest::tearDown

MyTest::setUp
MyTest::testTwo
MyTest::tearDown.
執行步驟:

MySuite::setUp

MyTest::setUp
MyTest::testOne
MyTest::tearDown

MyTest::setUp
MyTest::testTwo
MyTest::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.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.
# 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.
為什麼要知道測試涵蓋範圍?
找出尚未測試的部份
找出尚未測試的部份


程式碼測試的涵蓋範圍越廣,程
式就更加可靠
掌握開發進度
掌握開發進度


看著涵蓋範圍百分比的增加….
會很有成就感
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/)
謝謝大家

More Related Content

What's hot

使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務升煌 黃
 
Celery: The Distributed Task Queue
Celery: The Distributed Task QueueCelery: The Distributed Task Queue
Celery: The Distributed Task QueueRichard Leland
 
資訊安全入門
資訊安全入門資訊安全入門
資訊安全入門Tyler Chen
 
Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...
Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...
Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...Edureka!
 
Awesome Traefik - Ingress Controller for Kubernetes - Swapnasagar Pradhan
Awesome Traefik - Ingress Controller for Kubernetes - Swapnasagar PradhanAwesome Traefik - Ingress Controller for Kubernetes - Swapnasagar Pradhan
Awesome Traefik - Ingress Controller for Kubernetes - Swapnasagar PradhanAjeet Singh Raina
 
Advanced Reflection in Java
Advanced Reflection in JavaAdvanced Reflection in Java
Advanced Reflection in Javakim.mens
 
Jsp/Servlet
Jsp/ServletJsp/Servlet
Jsp/ServletSunil OS
 
SPAのルーティングの話
SPAのルーティングの話SPAのルーティングの話
SPAのルーティングの話ushiboy
 
How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...
How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...
How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...Lucas Jellema
 
微服務的基礎建設 - Service Discovery, Andrew Wu
微服務的基礎建設 - Service Discovery, Andrew Wu微服務的基礎建設 - Service Discovery, Andrew Wu
微服務的基礎建設 - Service Discovery, Andrew WuAndrew Wu
 
Advanced SQL injection to operating system full control (whitepaper)
Advanced SQL injection to operating system full control (whitepaper)Advanced SQL injection to operating system full control (whitepaper)
Advanced SQL injection to operating system full control (whitepaper)Bernardo Damele A. G.
 
PHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 とPHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 とdo_aki
 
Laravel の paginate は一体何をやっているのか
Laravel の paginate は一体何をやっているのかLaravel の paginate は一体何をやっているのか
Laravel の paginate は一体何をやっているのかShohei Okada
 
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用Shengyou Fan
 
[PHP 也有 Day #64] PHP 升級指南
[PHP 也有 Day #64] PHP 升級指南[PHP 也有 Day #64] PHP 升級指南
[PHP 也有 Day #64] PHP 升級指南Shengyou Fan
 
Injecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at RuntimeInjecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at RuntimeAjin Abraham
 
Spring security oauth2
Spring security oauth2Spring security oauth2
Spring security oauth2axykim00
 
PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發
PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發
PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發Shengyou Fan
 

What's hot (20)

使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務使用 Passkeys 打造無密碼驗證服務
使用 Passkeys 打造無密碼驗證服務
 
Celery: The Distributed Task Queue
Celery: The Distributed Task QueueCelery: The Distributed Task Queue
Celery: The Distributed Task Queue
 
資訊安全入門
資訊安全入門資訊安全入門
資訊安全入門
 
Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...
Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...
Lambda Expressions in Java | Java Lambda Tutorial | Java Certification Traini...
 
Awesome Traefik - Ingress Controller for Kubernetes - Swapnasagar Pradhan
Awesome Traefik - Ingress Controller for Kubernetes - Swapnasagar PradhanAwesome Traefik - Ingress Controller for Kubernetes - Swapnasagar Pradhan
Awesome Traefik - Ingress Controller for Kubernetes - Swapnasagar Pradhan
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Advanced Reflection in Java
Advanced Reflection in JavaAdvanced Reflection in Java
Advanced Reflection in Java
 
Java in 2 hours
Java in 2 hoursJava in 2 hours
Java in 2 hours
 
Jsp/Servlet
Jsp/ServletJsp/Servlet
Jsp/Servlet
 
SPAのルーティングの話
SPAのルーティングの話SPAのルーティングの話
SPAのルーティングの話
 
How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...
How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...
How and Why GraalVM is quickly becoming relevant for developers (ACEs@home - ...
 
微服務的基礎建設 - Service Discovery, Andrew Wu
微服務的基礎建設 - Service Discovery, Andrew Wu微服務的基礎建設 - Service Discovery, Andrew Wu
微服務的基礎建設 - Service Discovery, Andrew Wu
 
Advanced SQL injection to operating system full control (whitepaper)
Advanced SQL injection to operating system full control (whitepaper)Advanced SQL injection to operating system full control (whitepaper)
Advanced SQL injection to operating system full control (whitepaper)
 
PHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 とPHP と SAPI と ZendEngine3 と
PHP と SAPI と ZendEngine3 と
 
Laravel の paginate は一体何をやっているのか
Laravel の paginate は一体何をやっているのかLaravel の paginate は一体何をやっているのか
Laravel の paginate は一体何をやっているのか
 
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
以 Kotlin Multiplatform Mobile (KMM) 開發跨平台行動應用
 
[PHP 也有 Day #64] PHP 升級指南
[PHP 也有 Day #64] PHP 升級指南[PHP 也有 Day #64] PHP 升級指南
[PHP 也有 Day #64] PHP 升級指南
 
Injecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at RuntimeInjecting Security into vulnerable web apps at Runtime
Injecting Security into vulnerable web apps at Runtime
 
Spring security oauth2
Spring security oauth2Spring security oauth2
Spring security oauth2
 
PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發
PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發
PHPCon China 2016 - 從學徒變大師:談 Laravel 框架擴充與套件開發
 

Similar to PHPUnit 入門介紹

Phpunit入门 r2
Phpunit入门 r2Phpunit入门 r2
Phpunit入门 r2Baohua Cai
 
PHPUnit + Xdebug 单元测试技术
PHPUnit + Xdebug 单元测试技术PHPUnit + Xdebug 单元测试技术
PHPUnit + Xdebug 单元测试技术hoopchina
 
Testing in python 2.7.3
Testing in python 2.7.3Testing in python 2.7.3
Testing in python 2.7.3Wen Liao
 
PHPUnit slide formal
PHPUnit slide formalPHPUnit slide formal
PHPUnit slide formaljameslabs
 
The ruby way test
The ruby way testThe ruby way test
The ruby way testDeng Peng
 
GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结coderzh
 
由一个简单的程序谈起--之四
由一个简单的程序谈起--之四由一个简单的程序谈起--之四
由一个简单的程序谈起--之四yiditushe
 
PHP & MySQL 教學
PHP & MySQL 教學PHP & MySQL 教學
PHP & MySQL 教學Bo-Yi Wu
 
Java script测试之js unit ut
Java script测试之js unit utJava script测试之js unit ut
Java script测试之js unit utfangdeng
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型keelii
 
钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会Zhi Zhong
 
Asp.net mvc 培训
Asp.net mvc 培训Asp.net mvc 培训
Asp.net mvc 培训lotusprince
 
第一次用 PHPUnit 寫測試就上手
第一次用 PHPUnit 寫測試就上手第一次用 PHPUnit 寫測試就上手
第一次用 PHPUnit 寫測試就上手Yi-Ming Huang
 
课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘
课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘
课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘Liu Allen
 
部分PHP问题总结[转贴]
部分PHP问题总结[转贴]部分PHP问题总结[转贴]
部分PHP问题总结[转贴]wensheng wei
 
Java单元测试
Java单元测试Java单元测试
Java单元测试darlingshan
 
Javascript autoload
Javascript autoloadJavascript autoload
Javascript autoloadjay li
 
从问题开始,谈前端架构
从问题开始,谈前端架构从问题开始,谈前端架构
从问题开始,谈前端架构裕波 周
 

Similar to PHPUnit 入門介紹 (20)

Phpunit入门 r2
Phpunit入门 r2Phpunit入门 r2
Phpunit入门 r2
 
PHPUnit + Xdebug 单元测试技术
PHPUnit + Xdebug 单元测试技术PHPUnit + Xdebug 单元测试技术
PHPUnit + Xdebug 单元测试技术
 
Testing in python 2.7.3
Testing in python 2.7.3Testing in python 2.7.3
Testing in python 2.7.3
 
PHPUnit
PHPUnitPHPUnit
PHPUnit
 
PHPUnit slide formal
PHPUnit slide formalPHPUnit slide formal
PHPUnit slide formal
 
The ruby way test
The ruby way testThe ruby way test
The ruby way test
 
GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结
 
由一个简单的程序谈起--之四
由一个简单的程序谈起--之四由一个简单的程序谈起--之四
由一个简单的程序谈起--之四
 
PHP & MySQL 教學
PHP & MySQL 教學PHP & MySQL 教學
PHP & MySQL 教學
 
Sun java
Sun javaSun java
Sun java
 
Java script测试之js unit ut
Java script测试之js unit utJava script测试之js unit ut
Java script测试之js unit ut
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型
 
钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会
 
Asp.net mvc 培训
Asp.net mvc 培训Asp.net mvc 培训
Asp.net mvc 培训
 
第一次用 PHPUnit 寫測試就上手
第一次用 PHPUnit 寫測試就上手第一次用 PHPUnit 寫測試就上手
第一次用 PHPUnit 寫測試就上手
 
课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘
课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘
课题一:PHP5.3、PHP5.4的特性介绍与深度挖掘
 
部分PHP问题总结[转贴]
部分PHP问题总结[转贴]部分PHP问题总结[转贴]
部分PHP问题总结[转贴]
 
Java单元测试
Java单元测试Java单元测试
Java单元测试
 
Javascript autoload
Javascript autoloadJavascript autoload
Javascript autoload
 
从问题开始,谈前端架构
从问题开始,谈前端架构从问题开始,谈前端架构
从问题开始,谈前端架构
 

More from Jace Ju

What happens in laravel 4 bootstraping
What happens in laravel 4 bootstrapingWhat happens in laravel 4 bootstraping
What happens in laravel 4 bootstrapingJace Ju
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 
Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnitJace Ju
 
深入淺出 MVC
深入淺出 MVC深入淺出 MVC
深入淺出 MVCJace Ju
 
如何打造 WebMVC Framework - 基礎概念篇
如何打造 WebMVC Framework - 基礎概念篇如何打造 WebMVC Framework - 基礎概念篇
如何打造 WebMVC Framework - 基礎概念篇Jace Ju
 
Refactoring with Patterns in PHP
Refactoring with Patterns in PHPRefactoring with Patterns in PHP
Refactoring with Patterns in PHPJace Ju
 
Patterns in Library Design (套件設計裡的模式)
Patterns in Library Design (套件設計裡的模式)Patterns in Library Design (套件設計裡的模式)
Patterns in Library Design (套件設計裡的模式)Jace Ju
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介Jace Ju
 
Patterns in Zend Framework
Patterns in Zend FrameworkPatterns in Zend Framework
Patterns in Zend FrameworkJace Ju
 
Head First Zend Framework - Part 1 Project & Application
Head First Zend Framework - Part 1 Project & ApplicationHead First Zend Framework - Part 1 Project & Application
Head First Zend Framework - Part 1 Project & ApplicationJace Ju
 
常見設計模式介紹
常見設計模式介紹常見設計模式介紹
常見設計模式介紹Jace Ju
 
Web Refactoring
Web RefactoringWeb Refactoring
Web RefactoringJace Ju
 
jQuery 實戰經驗講座
jQuery 實戰經驗講座jQuery 實戰經驗講座
jQuery 實戰經驗講座Jace Ju
 
CSS 排版 - 基礎觀念篇
CSS 排版 - 基礎觀念篇CSS 排版 - 基礎觀念篇
CSS 排版 - 基礎觀念篇Jace Ju
 
PHP 防駭 - 基礎觀念篇
PHP 防駭 - 基礎觀念篇PHP 防駭 - 基礎觀念篇
PHP 防駭 - 基礎觀念篇Jace Ju
 
PHP 物件導向 - 基礎觀念篇
PHP 物件導向 - 基礎觀念篇PHP 物件導向 - 基礎觀念篇
PHP 物件導向 - 基礎觀念篇Jace Ju
 

More from Jace Ju (16)

What happens in laravel 4 bootstraping
What happens in laravel 4 bootstrapingWhat happens in laravel 4 bootstraping
What happens in laravel 4 bootstraping
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
Beginning PHPUnit
Beginning PHPUnitBeginning PHPUnit
Beginning PHPUnit
 
深入淺出 MVC
深入淺出 MVC深入淺出 MVC
深入淺出 MVC
 
如何打造 WebMVC Framework - 基礎概念篇
如何打造 WebMVC Framework - 基礎概念篇如何打造 WebMVC Framework - 基礎概念篇
如何打造 WebMVC Framework - 基礎概念篇
 
Refactoring with Patterns in PHP
Refactoring with Patterns in PHPRefactoring with Patterns in PHP
Refactoring with Patterns in PHP
 
Patterns in Library Design (套件設計裡的模式)
Patterns in Library Design (套件設計裡的模式)Patterns in Library Design (套件設計裡的模式)
Patterns in Library Design (套件設計裡的模式)
 
購物車程式架構簡介
購物車程式架構簡介購物車程式架構簡介
購物車程式架構簡介
 
Patterns in Zend Framework
Patterns in Zend FrameworkPatterns in Zend Framework
Patterns in Zend Framework
 
Head First Zend Framework - Part 1 Project & Application
Head First Zend Framework - Part 1 Project & ApplicationHead First Zend Framework - Part 1 Project & Application
Head First Zend Framework - Part 1 Project & Application
 
常見設計模式介紹
常見設計模式介紹常見設計模式介紹
常見設計模式介紹
 
Web Refactoring
Web RefactoringWeb Refactoring
Web Refactoring
 
jQuery 實戰經驗講座
jQuery 實戰經驗講座jQuery 實戰經驗講座
jQuery 實戰經驗講座
 
CSS 排版 - 基礎觀念篇
CSS 排版 - 基礎觀念篇CSS 排版 - 基礎觀念篇
CSS 排版 - 基礎觀念篇
 
PHP 防駭 - 基礎觀念篇
PHP 防駭 - 基礎觀念篇PHP 防駭 - 基礎觀念篇
PHP 防駭 - 基礎觀念篇
 
PHP 物件導向 - 基礎觀念篇
PHP 物件導向 - 基礎觀念篇PHP 物件導向 - 基礎觀念篇
PHP 物件導向 - 基礎觀念篇
 

PHPUnit 入門介紹