iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 12
0
Software Development

如何一步步實踐TDD (測試驅動開發)系列 第 12

Mock 與 範例四 (Mockery, PHP)

每個程式都必定會去呼叫其他的函式,但我們在編寫測試時,也許有些函式不能隨意呼叫(e.g. 對外部送出 request),或者函式的執行時間較長(e.g. 存取檔案或外部資源)。

這時我們可以利用 Mock 的工具來幫助測試。

簡單來說,就是對原本要執行的函數或物件,做一個假貨、呼叫假的函式、回傳假的值等等,來模擬並取代它實際會做的事情。

範例

讓我們拿 範例三 接著新增一些東西,假設我們要新增一個發薪日物件,而等待發薪水總是漫長的。

在範例三中,最長的測試執行時間也不超過0.1秒。

Mockery

Mockery 是 PHP 常用的 Mock 套件,可以利用 Composer 安裝:

$ composer require mockery/mockery --dev

或者如果是下載範例程式碼,記得在 git checkout <tagname> 之後透過 composer.json 安裝:

$ composer install

會把所有需要的套件裝在 vendor/ 中。

Payday::pay()

紅燈

<?php
use PHPUnit\Framework\TestCase;
use Src\Payday;

class PaydayTest extends TestCase
{
    public function testPaydayIsSlow()
    {
        $payday = new Payday();
        $paid_result = $payday->pay();
        $this->assertSame("paid", $paid_result[0]);
    }

( 註:不需要 use Mockery )

綠燈

<?php
namespace Src;

class Payday
{
    public function pay()
    {
        // It will do something so slowly
        sleep(2);
        
        return ['paid', 'is', 'so', 'slow'];
    }
}

我們故意在 pay() 中讓程式停止2秒,假裝這個函式要跑很多東西、執行了很久才回傳。

重新執行測試,當然也會發現因為執行了 pay() 而跑了兩秒多。

$ ./vendor/bin/phpunit

如果之後還有其他測試,都要呼叫到 pay() ,而我們並不需要每次都讓它完整執行,這時就可以用 Mock 物件來取代。

class PaydayTest extends TestCase
{
    public function testPaydayByMock()
    {
        $mock_payday = Mockery::mock(Payday::class);
        $mock_payday->shouldReceive('pay')
            ->once()
            ->andReturn(['paid', 'fast']);

        $paid_result = $mock_payday->pay();
        $this->assertSame("paid", $paid_result[0]);

        Mockery::close();
    }
}

( 範例程式碼$ git checkout 3e,包含以上兩種測試函式 )

利用Mockery::mock()來回傳 Payday 的 mock 物件。

shouldReceive('pay')是指參數內的這個函數 pay() 應該要被呼叫。

once()是只呼叫一次,若不吻合則會跳出 InvalidCountException,也可以設定成 twice() 或 times(n)。

andReturn()則是設定假的回傳值,接著我們對 mock_payday 物件做同樣的函式呼叫,就會模仿外觀行為,而不需要花費原本的執行時間。

記得最後面要加上 Mockery::close() 確保 mock 物件的關閉。

注意

Mock 有助於解除,在測試時物件之間的相依性。但也請謹慎使用,它終究只是個 Mock 物件,並沒有真的運行,不小心可能會忽略掉某些測試狀況。

以及在覆蓋率的部分,用 Mock 的部分因為沒有執行原本產品程式的物件跟函式,並不算入覆蓋率的計算,因此會發現不是原本的100% 了。

Code Coverage Report:
 Summary:
  Classes: 66.67% (2/3)
  Methods: 75.00% (3/4)

上一篇
Git 版本控制 與 Commit
下一篇
TDD 實戰:進入 Laravel
系列文
如何一步步實踐TDD (測試驅動開發)30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言