前言
Mockery 在 UnitTest 上非常常見,需要去模擬第三方套件的時候就會需要用到他,舉例來說像是 PHPMailer ,頭痛的地方有幾點
- 寫測試但不希望他寄信
- 我該怎麼去模擬他的 method 讓結果跟測試期望的一樣
為了 Coverage 100% 這時候就推薦使用 Mockery 了。
使用 Mockery
安裝套件
composer require --dev mockery/mockery
程式邏輯
- overwrite PHPMailer 並 mock 每個使用到的 method
- 實作 AddAddress, isHTML, Send 這三個 method
- AddAddress 參數說明
- shouldReceive: 需要去 mock 的 method
- atLeast: 這個 method 至少會執行幾次
- times(n): 執行 n 次,需搭配 atLeast 去使用
- withAnyArgs: 沒有限制他的傳入值
- isHTML 參數說明
- once: 只會被執行一次
- with(true): 只會帶入 true
- Send 參數說明
- andReturn(true): 只會 return true
- (重要)測試單元最後需ㄧㄠ透過 $this->tearDown() 去 reset mockery
案例分享
Mian Class - Main.php
class Main
{
protected $mail;
function __construct()
{
$this->PHPMailerInit();
}
public function run()
{
/*
* Main function .........
*/
$this->mail->Body = $html;
if (!$this->mail->Send()) {
return false;
}
return true;
}
protected function PHPMailerInit()
{
/* phpmailer */
$this->mail = new \PHPMailer;
$this->mail->CharSet = 'UTF-8';
$this->mail->From = 'sender@test.com';
$this->mail->AddAddress('hashman@test.com');
$this->mail->AddAddress('andy@test.com');
$this->mail->isHTML(true);
$this->mail->Subject = 'My subject';
}
}
Unit Test - MyTest.php
use \Mockery as m;
class MyTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$this->setUpDb();
$this->mockData();
}
public function testMailWillSend()
{
$mock = m::mock('overload:\PHPMailer');
$mock->shouldReceive('AddAddress')
->atLeast()
->times(1)
->withAnyArgs();
$mock->shouldReceive('isHTML')
->once()
->with(true);
$mock->shouldReceive('Send')
->andReturn(true);
$main = new Main();
$result = $main->run();
$this->assertEquals(true, $result, 'Email 寄出應該要回傳 true');
$this->tearDown();
}
public function testMailWontSend()
{
$mock = m::mock('overload:\PHPMailer');
$mock->shouldReceive('AddAddress')
->atLeast()
->times(1)
->withAnyArgs();
$mock->shouldReceive('isHTML')
->once()
->with(true);
$mock->shouldReceive('Send')
->andReturn(false);
$main = new Main();
$result = $main->run();
$this->assertEquals(false, $result, 'Email 無法寄出應該要回傳 false');
$this->tearDown();
}
protected function tearDown()
{
m::close();
}
}
ps. 依照各自環境的狀況自己去 require autoload.php
參考資料