兩天搞懂的簡單 CI(一)-Unit Test

以 PHPUnit 示範


前言

我之前一直想在公司的專案加上自動化測試,但是從建置環境開始就遭遇困難、加上團隊的成員及主管間沒有共識,最後是當時有時程上的壓力在,因此最後只在我自己的專案中加入了簡單的單元測試,確保在出問題時可以較快速的找出問題所在。

所以首先我要感謝 海總理克服人心的障礙 跨越自動化測試的門檻》這個 Talk 的投影片,讓我下定決心要建立 CI 的流程。(雖然弄好的當天接到震驚的消息)

至於為什麼是兩天搞懂呢?因為我真的花了兩天 XD


單元測試 (Unit Test)

單元測試顧名思義就是針對程式中的單元 (function, method, class 等) 做測試,這樣當某個區塊出錯時,可以迅速的找到哪個單元出錯了,減少花在通靈的時間。因為公司是使用 PHP 開發,所以我就使用了 PHPUnit 這套作為單元測試的套件。

安裝 PHPUnit

安裝的方法其實很簡單,就是起身走到系統工程師旁邊,對著他說「幫我建一個開發環境然後再裝個 PHPUnit,感謝」,接著走回座位等他回報完成。 #真人真事

安裝方法其實可以參考 官方文件,主要有兩種方法安裝:

  1. 直接下載
  2. 使用 composer 安裝

而公司因為環境的關係,就選用第一個方法直接下載,實際上也很簡單。

# 使用 wget 抓取檔案
$ wget https://phar.phpunit.de/phpunit-6.1.phar

# 增加可執行權限
$ chmod +x phpunit-6.1.phar

# 移至指令的資料夾,並改名為 phpunit
$ sudo mv phpunit-6.1.phar /usr/local/bin/phpunit

# 測試是否能執行
$ phpunit --help

按照上面的步驟如果沒出錯的話,應該就會出現 PHPUnit 的提示訊息,接下來我們就可以開始寫測試囉。

另外如果需要取得測試涵蓋率 (Coverage),則需要安裝額外的 PHP Extension Xdebug,安裝方法也不難

# 使用 pecl 安裝(也可以自己從 Source build)
$ pecl install xdebug

# 將以下加入 php.ini 中(需使用完整的路徑)
zend_extension="/usr/local/php/modules/xdebug.so"

安裝完畢後就可以使用 PHPUnit Coverage 的功能了。


測試程式範例

因為我才剛開始做測試,所以我在這裡只放些簡單的示範。

建立測試程式

首先建立一個 class 與一支測試程式。

<?php
class Article
{
    // 取得文章內容
    public function getArticle($id) {
        $article = $DB->getrow($id);
        return ($article) ?$article :array();
    }

    // 關閉文章
    public function disableArticle($id) {
        $DB->update($id, ['status' => 0]);
    }
}

接著就執行測試 ,如果功能都正常的話,應該會出現類似以下訊息

# phpunit articleTest.phpPHPUnit x.x.xx by Sebstian Bergmann

.

Time: 2 seconds
OK (2 test, 3 assertion)

以上是用 assertEquals 來測試是否等同預期值,更多的可以到 官方文件 查看。

由 dataProvider 提供資料

另外一點值得注意的地方是關於 dataProvider,當宣告了以後這個 function 的參數會從 dataProvider 輸入,例如上面的程式中

/**
 * @dataProvider articleProvider
 */
public function testGetArticle($id, $title, $status) {
    ...
}

public function articleProvider() {
    return [
        [1, '伐伐伐伐伐木工', 0],
        [2, '請給我工作', 1],
    ];
}

實際上的效果等同於以下,是個很有用的功能。

testGetArticle(1, '伐伐伐伐伐木工', 0)
testGetArticle(2, '請給我工作', 0)

編輯設定檔

當專案中有許多的單元時,不可能讓它一支一支去跑,因此我們可以在 repository 的根目錄中增加一個設定檔如 .phpunit_article.xml (檔案名稱可自訂)

<phpunit>
  <testsuites>
    <testsuite>
      <directory suffix=".php">class/article/</directory>
      <directory suffix=".php">class/something/</directory>
      <exclude>
        <directory>class/something/else/</directory>
      </exclude>
    </testsuite>
  </testsuites>

  <filter>
    <whitelist processUncoveredFilesFromWhitelist="true">
      <directory suffix=".php">class/article/</directory>
      <directory suffix=".php">class/something/</directory>
      <exclude>
        <directory>class/something/else/</directory>
        <file>class/article/useless.php</file>
      </exclude>
    </whitelist>
  </filter>
</phpunit>

在這個 xml 檔中,testsuites 讓 PHPUnit 知道要使用哪些測試檔,如果是 directory 的話則是使用目錄中所有 *Test.php 進行測試,而 filter whitelist 則是要做測試涵蓋率計算的檔案,這兩種都有 exclude 跟 suffix 等可以將不需要的檔案排除在外,這些一樣可以在 官方文件 中找到。


一條指令搞定測試

當我們測試程式寫完、測試設定也完成了以後,我們就可以用一條指令來完成測試並且得到類似於以下的結果。

$ phpunit --configuration .phpunit_article.xml --coverage-text
PHPUnit x.x.xx by Sebastian Bergmann and contributors.

.

Time: 3.67 seconds, Memory: 34.75MB

OK (4 tests, 12 assertions)

Code Coverage Report:
  2017-06-13 15:23:31

 Summary:
  Classes:  0.00% (0/9)
  Methods:  1.44% (2/139)
  Lines:   12.64% (269/2129)

xxx
  Methods:  10.00% ( 1/10)   Lines:  11.96% ( 11/ 92)
xxx
  Methods:   6.67% ( 1/15)   Lines:  66.49% (258/388)

總結

自動化測試是一種大家都覺得重要,很多人都想做,但是卻不一定有人想動手做的事情。並且怎樣才是好的測試,而非無效、湊數的測試呢?這點我還有很多東西要學,但是先動手寫了這篇記錄我怎麼開始使用 Unit Test,希望能幫助到其他想動手卻不知道怎麼開始的人。

本系列的下一篇將會講到 GitLab-CI。

延伸閱讀:Test Doubles — Fakes, Mocks and Stubs.