Mockery


先建立一個服務

  • 先於 app底下建立一個 Services資料夾

建立測試資料

  • Tests\Unit 底下建立一個 Services資料夾,在建立一個 ClientTest.php的檔案
  • 建立一個 test_query()的function
  • function 內建立連線且設定需送出與回傳的值
1
2
3
4
5
6
$guzzleClient->shouldReceive('request')->andReturn(
new Response(200, [], file_get_contents(__DIR__.'/result.txt'))
);

$client = new Client($guzzleClient, $log, $key);

  • 確認建立連線後,檢視所需得到的資料格式,在建立 ClientTest.php的檔案同層建立 result.txt假資料
  • 而後於檔案上方引入,並使用
1
use Mockery as m;
1
$guzzleClient = m::mock(GuzzleClient::class);
  • 最後放入測試資料的格式驗證
1
2
3
4
5
6
7
8
9
$this->assertArraySubset([
'price' => 9165.74788036,
'volume_24h' => 15782991288.054,
'percent_change_1h' => -0.537992,
'percent_change_24h' => -1.19578,
'percent_change_7d' => 16.4927,
'market_cap' => 162835578819.6922,
'last_updated' => '2019-06-18T12:14:22.000Z',
], $client->query('btC'));

回到服務資料夾內建立連線檔案

  • 建立連線的 Client 檔案,使用GuzzleHttp連線,並於建構子中依賴注入
  • 建立 query()透過key去發出 request ,並將取回的資料進行 json_decode(),於最後透過laravel collection的方法,filter 出符合的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public function __construct(GuzzleClient $client)
{
$this->client = $client;
}

public function query($symbol = 'BTC')

$response = $this->client->request('GET','https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest', [
'headers' => [
'X-CMC_PRO_API_KEY' => 'xxxxxxx',
],
]);

$results = json_decode($response->getBody()->getContents(), true);

return Arr::get(collect($results['data'])->filter(function ($item) use ($symbol) {
return $item['symbol'] === trim(strtoupper($symbol));
})->first(), 'quote.USD');
}

想要紀錄呼叫api次數

  • 由於紀錄log是呼出去,因此需使用 spy去監測紀錄
  • 於服務資料夾下建立 Log.php檔案,並建立 info()function
1
2
3
4
5
6
7
8
9
10
<?php
namespace App\Services;

class Log
{
public function info()
{

}
}
  • 在測試檔案中加上log , 也於連線方法上引入
1
2
$log = m::spy(Log::class);
$client = new Client($guzzleClient, $log);
  • 需在驗證連線上加上 once()
1
2
3
$guzzleClient->shouldReceive('request')->andReturn(
new Response(200, [], file_get_contents(__DIR__.'/result.txt'))
)->once();
  • 於測試程式最後加上
1
$log->shouldHaveReceived('info')->with('xxxxxxxx')->once();
  • 最後需在此方法之上加入 teardown() function
  • 此意思是代表 在每個測試後執行,另外也可以只在整個測試類別執行一次的
1
2
3
4
5
protected function tearDown():void
{
parent::tearDown();
m::close();
}
  • 最後在連線的建構子上加上
1
2
3
4
5
public function __construct(GuzzleClient $client, Log $log)
{
$this->client = $client;
$this->log = $log;
}
  • query()加上
1
$this->log->info('xxxxxxxx');

加上環境檔的key,使用依賴注入

  • Providers資料夾內的 AppServiceProvider加上
1
2
3
4
5
6
public function register()
{
$this->app->singleton(Client::class, function() {
return new Client(new GuzzleClient, new Log(), env('CMC_API_KEY'));
});
}
  • 連線檔案的建構子加上key依賴注入
1
2
3
4
5
6
public function __construct(GuzzleClient $client, Log $log, $key = null)
{
$this->client = $client;
$this->log = $log;
$this->key = $key;
}
  • query()內也更改key的引入
1
2
3
4
5
6
7
$this->log->info($this->key);

$response = $this->client->request('GET','https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest', [
'headers' => [
'X-CMC_PRO_API_KEY' => $this->key,
],
]);
  • 測試檔案key的更改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public function test_query() //this test failed
{
$key = 'xxx';
$guzzleClient = m::mock(GuzzleClient::class);
$guzzleClient->shouldReceive('request')->andReturn(
new Response(200, [], file_get_contents(__DIR__.'/result.txt'))
)->once();

$log = m::spy(Log::class);
$client = new Client($guzzleClient, $log, $key);
// assertions

$this->assertArraySubset([
'price' => 9165.74788036,
'volume_24h' => 15782991288.054,
'percent_change_1h' => -0.537992,
'percent_change_24h' => -1.19578,
'percent_change_7d' => 16.4927,
'market_cap' => 162835578819.6922,
'last_updated' => '2019-06-18T12:14:22.000Z',
], $client->query('btC'));

$log->shouldHaveReceived('info')->with($key)->once();
}

為何要使用測試

  • 寫測試不是為了驗證結果,而是為了驗證過程
  • 要用什麼測試,假資料或是等等等,須自行摸索