@這是小豪的第十四篇文章
最近在學習超哥的 《PHP 擴充套件包實戰教程 - 從入門到釋出》,寫到單元測試的時候發現了一個新東西
Mockery
,雖然有使用的範例,但是看的我還是暈暈的不曉得是個啥子意思,到底什麼是Mockery
?究竟怎麼用呢?今天就來給大家簡單的分享一下。
準備
有時候我們需要對專案中用到第三方 API 的地方做一些測試,但往往這個時候就很為難,如果說直接發起請求的話一般是需要付費的或者減少了呼叫次數,再者說你電腦沒有網路,那豈不是搞不定了,哈哈,為了讓測試更加的優雅,Mockery
就應時而生了。
說的直觀一點,Mockery
就是用來模仿你要執行的程式碼。比如說某段程式碼中有部分是需要傳送網路請求,那麼你就可以利用 Morckery
建立一個模擬請求,並給定這個請求的返回值(權當它是能夠通過請求,並能獲取到返回值的),這樣子就發揮了 Mockery
的真正作用。
如果說確確實實要真實的資料測試,那就只有老老實實請求了。
小案例
好,說了這麼多了,我們來實戰一下,我們就拿超哥的 overtrue/weather 來看:
class Weather
{
...
/**
* @var string
*/
protected $key;
/**
* Weather constructor.
*
* @param string $key
*/
public function __construct($key)
{
$this->key = $key;
}
/**
* @return \GuzzleHttp\Client
*/
public function getHttpClient()
{
return new Client($this->guzzleOptions);
}
/**
* @param string $city
* @param string $type
* @param string $format
*
* @return \Psr\Http\Message\ResponseInterface
*
* @throws \Overtrue\Weather\Exceptions\HttpException
* @throws \Overtrue\Weather\Exceptions\InvalidArgumentException
*/
public function getWeather($city, $type = 'base', $format = 'json')
{
$url = 'https://restapi.amap.com/v3/weather/weatherInfo';
if (!\in_array(\strtolower($format), ['xml', 'json'])) {
throw new InvalidArgumentException('Invalid response format: '.$format);
}
if (!\in_array(\strtolower($type), ['base', 'all'])) {
throw new InvalidArgumentException('Invalid type value(base/all): '.$type);
}
$format = \strtolower($format);
$type = \strtolower($type);
$query = array_filter([
'key' => $this->key,
'city' => $city,
'output' => $format,
'extensions' => $type,
]);
try {
$response = $this->getHttpClient()->get($url, [
'query' => $query,
])->getBody()->getContents();
return 'json' === $format ? \json_decode($response, true) : $response;
} catch (\Exception $e) {
throw new HttpException($e->getMessage(), $e->getCode(), $e);
}
}
...
}
可以看到在 getWeather 方法中是存在網路請求的,如何去模擬這個 http client 呢,現在,我們來看看 Mockery
是如何實現的。
實現的程式碼其實很簡單,我們先直接來看,然後我再一一解釋:
class WeatherTest extends TestCase
{
.
.
.
public function testGetWeather()
{
// 建立模擬介面響應值。
$response = new Response(200, [], '{"success": true}');
// 建立模擬 http client。
$client = \Mockery::mock(Client::class);
// 指定將會產生的形為(在後續的測試中將會按下面的引數來呼叫)。
$client->allows()->get('https://restapi.amap.com/v3/weather/weatherInfo', [
'query' => [
'key' => 'mock-key',
'city' => '深圳',
'output' => 'json',
'extensions' => 'base',
]
])->andReturn($response);
// 將 `getHttpClient` 方法替換為上面建立的 http client 為返回值的模擬方法。
$w = \Mockery::mock(Weather::class, ['mock-key'])->makePartial();
$w->allows()->getHttpClient()->andReturn($client); // $client 為上面建立的模擬例項。
// 然後呼叫 `getWeather` 方法,並斷言返回值為模擬的返回值。
$this->assertSame(['success' => true], $w->getWeather('深圳'));
}
.
.
.
我們首先看到的是:建立 http client 模擬 =>\Mockery::mock(Client::class);
,是的這裡就模擬了一個 http client ,哈哈,假的。既然模擬了,我們該怎麼用呢,我們再來看:
// 指定將會產生的形為(在後續的測試中將會按下面的引數來呼叫)。
$client->allows()->get('https://restapi.amap.com/v3/weather/weatherInfo', [
'query' => [
'key' => 'mock-key',
'city' => '深圳',
'output' => 'json',
'extensions' => 'base',
]
])->andReturn($response);
簡單的解讀一下上面的程式碼:利用模擬的 http client 通過 allows 方法,呼叫 http client 中的 get 方法並通過 andReturn 給定自己設定的返回值。什麼意思呢,就是說假定這個網路請求的結果通過且結果為 $response;
我們繼續往下看:
// 將 `getHttpClient` 方法替換為上面建立的 http client 為返回值的模擬方法。
$w = \Mockery::mock(Weather::class, ['mock-key'])->makePartial();
$w->allows()->getHttpClient()->andReturn($client); // $client 為上面建立的模擬例項。
// 然後呼叫 `getWeather` 方法,並斷言返回值為模擬的返回值。
$this->assertSame(['success' => true], $w->getWeather('深圳'));
可以看到程式碼中模擬了一個 Weather
物件, mock-key
為注入的 key,因為是模擬的所以也不需要太在意真實性。可以看到後面追加了一個 makePartial
方法,這個方法是幹嘛用的呢,就是 只 mock 部分方法
,什麼意思呢,就拿下面的程式碼來說,通過模擬物件呼叫了 getHttpClient
方法,並給定假定的返回值,這裡呼叫了 getHttpClient
方法,就只 mock 了 getHttpClient
這個方法,就是這個意思,哈哈。
我們繼續往下看,因為已經模擬了網路請求的返回值,所以我們接下來通過 getWeather
獲取的返回值也就是上面的 $response
。
至此,一個簡單的 Mockery
模擬就結束拉,不曉得大家有木有清楚究竟如何去使用。
常用方法
-
shouldReceive()
:需要呼叫的方法,就拿上面的例子來說如果需要呼叫Weather
中的getHttpClient
方法,就可以通過shouldReceive('getHttpClient')
來實現,並不僅僅只有allows()
能實現的。 -
once()
:只呼叫一次。 -
with()
:應該被傳入的引數。 -
andReturn()
:假定的返回值。 andThrow()
:丟擲異常。
結束語
看完上面的案例之後,大家心裡多多少少也對 Mockery
有個大致的瞭解了,這裡沒有深入的挖掘 Mockery
是如何實現的,後面有機會再來深究一下。先用起來,方便測試,哈哈。
看完超哥的擴充套件包教程之後,自己也嘗試著寫了一個擴充套件包 《快遞物流查詢-快遞查詢介面元件》,寫到這裡發現自己擴充套件包中的 Mockery
測試貌似寫的有點問題,待會就去修復了,哈哈。寫的很粗糙,但是也是自己跨出的第一步,哈哈,開心。要是有個小星星就更開心了,哈哈。
感謝
-
感謝 @icecho 對我的第一個擴充套件包做熱心指導。
相關連結
Mockery
github 連結:https://github.com/mockery/mockery- 超哥擴充套件包教程連結:https://learnku.com/courses/creating-package
- 小豪的第一個擴充套件包連結:https://github.com/finecho/logistics-inquiry