PHPUnit

出自 Z
前往:導覽搜尋


Test Case

Test case 需要繼承 PHPUnit_Framework_TestCase,類別名稱需要以 Test 結尾。

someTest.php: <syntaxhighlight lang="php"> <?php

class SomeTest extends PHPUnit_Framework_TestCase{ } </syntaxhighlight>


每個 unit test 方法必須是 public method 且以 test 開頭: <syntaxhighlight lang="php"> <?php

class SomeTest extends PHPUnit_Framework_TestCase{

  public function testSomething(){
     $this->assertTrue(true);
  }

} </syntaxhighlight>


phpunit 執行測試: <syntaxhighlight lang="text"> $ phpunit someTest.php PHPUnit 3.7.19 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 3.25Mb

OK (1 test, 1 assertion) </syntaxhighlight>


程式中可以有多個 test case,phpunit 按順序執行測試。

someTest.php: <syntaxhighlight lang="php"> <?php

class SomeTest extends PHPUnit_Framework_TestCase {

  public function testSomething(){
     $this->assertTrue(true);
  }
  public function testAnotherthing() {
     $this->assertFalse(false);
  }

}

</syntaxhighlight>

執行測試: <syntaxhighlight lang="text"> $ phpunit someTest.php PHPUnit 3.7.19 by Sebastian Bergmann.

..

Time: 1 second, Memory: 3.25Mb

OK (2 tests, 2 assertions) </syntaxhighlight>


Assertion

PHPUnit 內建多種 assertion,如 assertTrue()、assertFalse()、assertEquals() 等。 <syntaxhighlight lang="php"> public function testSomething(){

  $this->assertEquals('foo', 'bar');   // make assertion fail

} </syntaxhighlight>

<syntaxhighlight lang="diff"> $ phpunit someTest.php PHPUnit 3.7.19 by Sebastian Bergmann.

F

Time: 0 seconds, Memory: 3.50Mb

There was 1 failure:

1) SomeTest::testSomething Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'foo' +'bar'

someTest.php:5

FAILURES! Tests: 1, Assertions: 1, Failures: 1. </syntaxhighlight>

Annotation

Dependency

透過「@depends」來標示 test case 之間的相依關係。若相依性沒有滿足,PHPUnite 則會自動跳過測試。

testTwo() 相依在 testOne() 上: <syntaxhighlight lang="php"> class SomeTest extends PHPUnit_Framework_TestCase {

  public function testOne(){
     $this->assertTrue(false);
  }
  /**
   * @depends testOne
   */
  public function testTwo(){
     $this->assertTrue(true);
  }

} </syntaxhighlight>

testOne() asserts false,所以 PHPUnit 會自動跳過 testTwo():

$ phpunit someTest.php
PHPUnit 3.7.19 by Sebastian Bergmann.

FS

Time: 1 second, Memory: 3.25Mb

There was 1 failure:

1) SomeTest::testOne
Failed asserting that false is true.

someTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1, Skipped: 1.


「@depends」也可以跨類別檔做定義,但是 PHPUnite 沒有辦法調整測試執行順序,還是需要撰寫 test case 自行注意。 tests/FailedToTest.php <syntaxhighlight lang="php"> class FailToTest extends PHPUnit_Framework_TestCase {

  public function testFail(){
     $this->assertTrue(false);  // assert failed
  }

} </syntaxhighlight> tests/OtherTest.php <syntaxhighlight lang="php"> class OtherTest extends PHPUnit_Framework_TestCase {

  /**
   * @depends testFail
   */
  public function testOther(){
     $this->assertTrue(true);
  }

} </syntaxhighlight> 執行測試時,若先執行 testFial() 再執行到 testOther(),phpunit 會自動因為相依性沒有被滿足而略過 testOther() 不做測試。

Data Provider

若有多組數據要輸入做測試,可以利用 data provider 功能。

用「@dataProvider」標示測試資料要使用的提供函式,PHPUnit 執行測試時便會呼叫該函式,並依序將各組資料帶入測試: <syntaxhighlight lang="php"> class AddTest extends PHPUnit_Framework_TestCase {

  /**
   * @dataProvider provider
   */
  public function testAdder($a, $b, $expection) {
     $this->assertEquals(
        $expection, add($a, $b)
     );
  }
  function provider() {
     return array(
        array(1, 1, 2),      // 1 + 1 = 2
        array(1, -1, 1),      // 1 + -1 = 0, shoud assert false
        array(2147483648, 2147483648, 4294967296), // 2^31 + 2^31 = 2^32
     );
  }

} </syntaxhighlight>

PHPUnit 執行了三次 test case,第 2 次 assertion (1 + -1 = 1) 失敗:

$ phpunit addTest.php
PHPUnit 3.7.19 by Sebastian Bergmann.

.F.

Time: 0 seconds, Memory: 3.50Mb

There was 1 failure:

1) AddTest::testAdder with data set #1 (1, -1, 1)
Failed asserting that 0 matches expected 1.

addTest.php:17

FAILURES!
Tests: 3, Assertions: 3, Failures: 1.

Exception

在部份情況下,會希望程式丟出 exception,此時可以加上「@expectedException」。

以下二個 test case 期望值與回傳值都是 true,testTwo() 預期會收到 InvalidArgumentException,但程式中並沒有丟出例外: <syntaxhighlight lang="php"> class SomeTest extends PHPUnit_Framework_TestCase {

  public function testOne() {
     $this->assertTrue(true);
  }
  /**
   * @expectedException InvalidArgumentException
   */
  public function testTwo(){
     $this->assertTrue(true);
  }

} </syntaxhighlight>


執行測試,testTwo() 因為程式沒有丟出例外而 asserts false:

$ phpunit someTest.php
PHPUnit 3.7.19 by Sebastian Bergmann.

.F

Time: 0 seconds, Memory: 3.25Mb

There was 1 failure:

1) SomeTest::testTwo
Failed asserting that exception of type "InvalidArgumentException" is thrown.


FAILURES!
Tests: 2, Assertions: 3, Failures: 1.

Fixture

做測試時,必須控制讓測試環境一致,這些固定的環境設定稱作 fixture。

PHPUnit 會在測試前執行 setUp()、結束後執行 tearDown(),可以用來設定 fixtures: <syntaxhighlight lang="php"> <?php class SomeTest extends PHPUnit_Framework_TestCase {

 private $config;
 protected function setUp(){
   echo "setting up ...\n";
   $this->config = array('some config');
 }
 protected function tearDown(){
   echo "tearing down ...\n";
   $this->config = null;
 }
 public function testSomething(){
   $this->assertTrue(true);
 }
 public function testSomethingElse(){
   $this->assertTrue(true);
 }

} </syntaxhighlight>

setUp() 和 tearDown() 會在每個測試前後執行: <syntaxhighlight lang="text"> $ phpunit SomeTest.php PHPUnit 3.4.5 by Sebastian Bergmann.

setting up ... tearing down ... .setting up ... tearing down ... .

Time: 0 seconds, Memory: 7.75Mb

OK (2 tests, 2 assertions) </syntaxhighlight>

Test Organizing

通常 PHPUnit 需要指定 test case:

phpunit myTest.php

但也可以利用檔案結構來讓 PHPUnit 自動執行多個 test case。

將 test case 與程式分開放在不同目錄:

$ tree
.
├── lib
│   └── text.php
├── phpunit.xml_
└── tests
    └── textTest.php

執行 PHPUnit 設定 test case 所在目錄:

$ phpunit tests/
PHPUnit 3.7.19 by Sebastian Bergmann.

..........

Time: 1 second, Memory: 3.75Mb 

OK (10 tests, 10 assertions)


也可以使用設定檔 (預設是 phpunit.xml,可以用 phpunit -c 指定不同名稱的設定檔),設定需要執行的 test case。

phpunit.xml : <syntaxhighlight lang="xml"> <phpunit>

  <testsuites>
     <testsuite name="Text">
        <file suffix="Test.php">tests/TextTest.php</file>
     </testsuite>
  </testsuites>

</phpunit> </syntaxhighlight>

Database Testing

做資料庫測試改用 PHPUnit_Extensions_Database_TestCase 類別: <syntaxhighlight lang="php"> <?php require_once('PHPUnit/Extensions/Database/TestCase.php');

class DbTest extends PHPUnit_Extensions_Database_TestCase { } </syntaxhighlight>

繼承類別後需要實做二個 abstract method:getConnection()、getDataSet(): <syntaxhighlight lang="php"> class DbTest extends PHPUnit_Extensions_Database_TestCase {

  public function getConnection(){
  }
  public function getDataSet(){
  }

} </syntaxhighlight>


DataSet

  • Database DataSet
  • Query DataSet
  • Replacement DataSet
  • DataSet Filter
  • Composite DataSet



DataSet Filter

可以將 Database DataSet 過濾需要、不需要的資料表或欄位。

假設原有資料為: <syntaxhighlight lang="xml"> <dataset>

  <post id="1" user="Some One" content="blabla...." />

</dataset> </syntaxhighlight>


經過 DataSetFilter 過濾欄位白名單: <syntaxhighlight lang="php"> <?php require_once('PHPUnit/Extensions/Database/DataSet/QueryDataSet.php'); require_once('PHPUnit/Extensions/Database/DataSet/DataSetFilter.php');

class ATest extends PHPUnit_Extensions_Database_TestCase {

  ...
  public function testFilter(){
     $dataset = $this->getConnection()->createDataSet();
     $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataset);
     $filterDataSet->setExcludeColumnsForTable('post', array('content'));
     echo $filterDataSet;
  }

} </syntaxhighlight>

欄位「id」、「user」會留下,「content」會被過濾掉:

+----------------------+----------------------+
| post                                        |
+----------------------+----------------------+
|          id          |         user         |
+----------------------+----------------------+
|          1           |       Some One       |
+----------------------+----------------------+


可用的 filter:

  • addIncludeTables
  • addExcludeTables
  • setIncludeColumnsForTable
  • setExcludeColumnsForTable

DB Assertions

僅 PHPUnit_Extensions_Database_TestCase 類別可用:

  • assertTablesEqual($expected, $actual, $message)
  • assertDataSetsEqual($expected, $actual, $message)

Notes

Test Doubles

當程式有相依的物件時,PHPUnit 可以建立虛擬物件來控制物件的行為、回傳值。


Stubbing

Caculator 類別包含一個 add 方法: <syntaxhighlight lang="php"> <?php

class Cal {

 function add($a, $b){
   return $a + $b;
 }

} </syntaxhighlight>

建立 stub: <syntaxhighlight lang="php"> <?php

require('cal.php');

class CalTest extends PHPUnit_Framework_TestCase {

 public function testAdder(){
   // 建立 stub
   $stub = $this->getMock('Cal');
   // 沒有設定 add() 的回傳值,預設回傳 NULL
   var_dump( $stub->add(1, 2) );
 }

} </syntaxhighlight>


Stubbing add() 方法: <syntaxhighlight lang="php"> public function testAdder(){

 $stub = $this->getMock('Cal');
 // add() 被呼叫時一律回傳 1
 $stub->expects($this->any())
      ->method('add')
      ->will($this->returnValue(1));
 var_dump( $stub->add(1, 2) );

} </syntaxhighlight>

Mocking

Configure

PHPUnit 可以將設定寫在 XML 設定檔中,執行時自動載入設定,預設設定檔是 phpunit.xml。

phpunit.xml <syntaxhighlight lang="xml"> <phpunit> </phpunit> </syntaxhighlight>

PHPUnit Settings

PHPUnit 的設定。 <syntaxhighlight lang="xml"> <phpunit

  stopOnError="false"
  stopOnFailure="false"
  stopOnIncomplete="false"
  stopOnSkipped="false"
  colors="true"
  bootstrap="bootstrap.php">

</phpunit> </syntaxhighlight>

Test Suite

建立 test suites。 <syntaxhighlight lang="xml"> <phpunit>

  <testsuites>
     <testsuite name="library">
        <directory suffix=".php">tests/lib</directory>
     </testsuite>
  </testsuites>

</phpunit> </syntaxhighlight>


PHP ENV

設定 PHP 執行時要使用的全域變數或是環境參數。 <syntaxhighlight lang="xml"> <phpunit>

  <php>
     <cont name="HOST" value="my.host" />
     
  </php>

</phpunit> </syntaxhighlight> 以上設定相當於: <syntaxhighlight lang="php"> <?php

  define('HOST', 'my.host');
  $GLOBALS['DB_User'] = 'user';

</syntaxhighlight>


需要注意的地方,環境設定的載入順序為:

  1. phpunit.xml 變數
  2. boostrap.php


因此在 phpunit.xml 中所設定的變數,會將 bootstrap.php 的變數覆蓋。

Coverage Analysis

Coverage Analysis 可以分析 test case 所涵蓋的範圍,包括已測試的 methods、lines。

使用此功能需要先安裝 Xdebug

<syntaxhighlight lang="text"> $ phpunit --coverage-text tests/StrTest.php PHPUnit 3.7.19 by Sebastian Bergmann.

...

Time: 1 second, Memory: 5.50Mb

OK (3 tests, 3 assertions)


Code Coverage Report

 2013-06-07 17:16:55
Summary:
 Classes: 0.00% (0/1)
 Methods: 33.33% (1/3)
 Lines:   60.00% (3/5)

Str

 Methods:  66.67% ( 2/ 3)   Lines:  60.00% (  3/  5)

</syntaxhighlight>

Reference

Related Tools