在開發程式時,為了確保程式的正確性,一定都會使用到單元測試。但撰寫單元測試的方法因人而異,有時候土炮起來寫測試程式的時間還比正常開發的時間還要長,這時候就必須找個適合的工具幫忙處理測試的問題。PHPUnit就是負責解決這樣的狀況。

安裝 (可參考 安裝文件)

wget https://phar.phpunit.de/phpunit.phar
chmod +x phpunit.phar
mv phpunit.phar /usr/local/bin/phpunit

PS:為了避免程式找不到PHPUnit的class,最好是用PHP的get_include_path()檢查path是不是有引入PEAR的路徑(如果沒有可以到php.ini新增)

撰寫 PHPUnit 測試程式
首先建立一個簡單的程式,負責加總傳入的n到m總合

Math.php

class Math {
    function sum($min, $max){
        $total = 0;
        for($i = $min; $i <= $max; $i++){
            $total += $i;
        }
        return $total;
    }
}

接著撰寫PHPUnit測試程式

MathTest.php

//引入要測試的程式
require "Math.php";
class MathTest extends PHPUnit_Framework_TestCase
{
    //測試function的命名為test + function name
    public function testsum()
    {
        //assertEquals為判斷值是否相同,55為正確的結果,比對傳入sum(1,10)時,是否傳出55
        $this -> assertEquals(55,Math::sum(1,10));
        $this -> assertEquals(5050,Math::sum(1,100));
    }
}

寫好測試程式之後,就可以透過指令測試

phpunit MathTest.php
#如果正確的話,應該會輸出OK (1 test, 2 assertion),代表一組測試二筆測資

Data Provider
上面的例子也可以改成以 Data Provider的方式餵資料進去,這樣一來就不必寫很多次assertEquals

MathTest.php

//引入要測試的程式
require "Math.php";
class MathTest extends PHPUnit_Framework_TestCase
{
    //利用PHPUnit @dataProvider annotation來引用自訂的DataProvider
    /**
     *  @dataProvider sumDataProvider
     */
    public function testsum($expected, $min, $max)
    {
        $this -> assertEquals($expected,Math::sum($min,$max));
    }
    public function sumDataProvider(){
        return array(
            array(55,1,10),
            array(5050,1,100),
        );
    }
}

PS:Data Provider提供的資料都會被當成一組test,所以測試後應該會出現OK (2 tests, 2 assertions)

setUP、tearDown
setUP和tearDown是執行PHPUnit時預設執行的函式,主要是用來初始化設定及結束設定用的,執行順序為:setUp、test*、tearDown

MathTest.php

require "Math.php";
class MathTest extends PHPUnit_Framework_TestCase
{
    protected $_object;
    protected function setUp(){
        //開始測試時產生物件
        $this -> _object = new Math();
    }
    /**
     *  @dataProvider sumDataProvider
     */
    public function testsum($expected, $min, $max)
    {
        $this -> assertEquals($expected,$this -> _object -> sum($min,$max));
    }
    public function sumDataProvider(){
        return array(
            array(55,1,10),
            array(5050,1,100),
        );
    }
    protected function tearDown(){
        //測試結束時結束物件
        $this -> _object = null;
    }
}

上面的範例執行順序為:1.setUp、testsum(55,1,10)、tearDown 2.setUp、testsum(5050,1,100)、tearDown

測試錯誤:expectedException
在某些時候必須去測試錯誤,因為當使用者輸入錯的資料時,程式沒有噴出Exception,這樣的程式也是有問題的,PHPUnit可以透expectedException annotation 來測試預期的錯誤(要傳出正確的Exception才算測試成功)

ExceptionTest.php

class ExceptionTest extends PHPUnit_Framework_TestCase{
    /**
     * @expectedException BadFunctionCallException
     */
    public function testException(){
        try {
            //dosomething
        } catch(BadFunctionCallException $e){
            throw $e;
        }
    }
}

套件測試:Suite
若一個專案有多個測試時,可以透過Suite將所有測試程式包成套件測試

AllTest.php

require "ExceptionTest.php";
require "MathTest.php";

class AllTest extends PHPUnit_Framework_TestCase
{
    public static function suite(){
        $suite = new PHPUnit_Framework_TestSuite('SuiteName');
        //加入class名稱
        $suite -> addTestSuite('ExceptionTest');
        $suite -> addTestSuite('MathTest');
        return $suite;
    }
}

也可以將多個套件再包裝成一組套件

All2Test.php

require "AllTest.php";

class All2Test extends PHPUnit_Framework_TestCase
{
    public static function suite(){
        $suite = new PHPUnit_Framework_TestSuite('SuiteName');
        //加入套件
        $suite -> addTest(AllTest::suite());
        return $suite;
    }
}

phpunit.xml
除了使用程式包裝成套件之外,也可以透過phpunit.xml來完成

phpunit.xml

<!-- colors為true可以讓測試結果加上顏色方塊,bootstrap可以自行定義init程式(例如define一些變數) -->
<phpunit colors="true" bootstrap="init.php">
    <testsuite name="TestSuite">
        <directory>./tests</directory>
    </testsuite>
</phpunit>

測試

#使用phpunit指令,預設會按照phpunit.xml執行
phpunit

#指定執行其他檔名的xml
phpunit -c xxx.xml

#也可以在phpunit.xml同一層目錄下直接使用資料夾名稱
phpunit <directory>

另外要注意,phpunit.xml的目錄是判斷 xxxTest的程式才會執行,所以命名時要特別注意一下

相關資料可以參考 PHPUnit文件

Categories: PHP