Medium 清新閱讀版:連結
今天要來為大家介紹 Storage Mocking 及 HTTP Mocking!
Storage::fake():當我們希望在執行測試目標行為時,想驗證 Storage 各類行為是否符合預期,但又不要真的增刪改檔案時,可在測試程式碼中呼叫此函數。UploadedFile::fake():這個函數可以模擬出一個已上傳的檔案。Storage::disk($disk)->assertExists():可驗證指定的 $disk 內,是否有指定檔案存在。需在執行 Storage::fake() 後方可使用。Storage::disk(disk)->assertMissing():可驗證指定的 $disk 內,是否無指定檔案存在。需在執行 Storage::fake() 後方可使用。測試目標:上傳檔案端點
config/filesystems.php
<?php
return [
    'default' => env('FILESYSTEM_DISK', 'photos'),
    'disks' => [
        'photos' => [
            'driver' => 'local',
            'root' => storage_path('app/photos'),
            'throw' => false,
        ],
    ],
];
routes/api.php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
Route::post('/photos', function (Request $request) {
    $photo = $request->file('photo');
    if (empty($photo)) {
        return response()->json([], 422);
    }
    Storage::disk('photos')->put('photo.jpg', $photo);
    return response()->json('');
})->name('upload.photo');
測試程式碼:
<?php
namespace Tests\Feature;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class StorageTest extends TestCase
{
    public function testUploadFileSuccess()
    {
        Storage::fake('photos');
        $response = $this->json('POST', route('upload.photo'), [
            'photo' => UploadedFile::fake()->image('p.jpg'),
        ]);
        Storage::disk('photos')->assertExists('photo.jpg');
    }
    public function testUploadFileFailed()
    {
        Storage::fake('photos');
        $response = $this->json('POST', route('upload.photo'), [
            'p' => UploadedFile::fake()->image('p.jpg'),
        ]);
        Storage::disk('photos')->assertMissing('photo.jpg');
    }
}
以上測試程式碼,測試了 2 種測試案例:
testUploadFileSuccess():在這個測試案例函數中,我們驗證了當上傳照片端點被請求,且檔案內容有被放置在指定的欄位時,上傳之檔案將存在於 photos 這個 $disk中。testUploadFileFailed():在這個測試案例函數中,我們驗證了當上傳照片端點被請求,且檔案內容沒有被放置在指定的欄位時,上傳之檔案將不存在於 photos 這個 $disk中。接著我們來看看 HTTP Mocking 吧!
Http::fake():當預進行測試的的測試目標行為,其內部有向外部發送 API 請求,但不希望在執行測試時,真的向外部發送 API 請求時,可在測試程式碼中呼叫此函數。與其他 fake() 函數不同的是,HTTP Mocking 主要是設定在 Http::fake() 這個函數:
Http::fake([
	'https://weather.tw/taiwan' => Http::response(
		[
			'rain' => 0.8,
			'temp_high' => 27,
			'temp_low' => 23,
		], 
		200,
		[]
	),
]);
由上面的程式碼可看出,會吃一個陣列,而這個陣列就是預期中要模擬的「呼叫外部API」的行為及其回應。
測試目標:呼叫氣象局API索取天氣資料並統整
routes/api.php
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Route;
Route::get('rain-chance', function (Request $request) {
    $response = Http::get('https://weather.tw/taiwan');
    // 這邊我們先假設真的有這個網站
    // 且其回應值如下:
    // {"rain":0.8,"temp_high":27,"temp_low":23}
    if ($response->status() !== 200) {
        return response()->json(['Error' => 'Failed to get the data of rain chance.']);
    }
    $rainChance = $response->json('rain');
    if (!is_float($rainChance)) {
        return response()->json(['Error' => 'Failed to get the data of rain chance.']);
    }
    return response()->json(['rain_chance' => $rainChance]);
})->name('get.rain-chance');
測試程式碼:
<?php
namespace Tests\Feature;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class HttpTest extends TestCase
{
    public function testGetRainChanceCanWorkWhenGetValidData()
    {
        Http::fake([
            'https://weather.tw/taiwan' => Http::response(
                [
                    'rain' => 0.8,
                    'temp_high' => 27,
                    'temp_low' => 23,
                ],
                200,
                []
            ),
        ]);
        $this->get(route('get.rain-chance'))
            ->assertOk()
            ->assertJsonStructure(['rain_chance']);
    }
    public function testGetRainChanceCanWorkWhenGetInvalidData()
    {
        Http::fake([
            'https://weather.tw/taiwan' => Http::response(
                [
                    'temp_high' => 27,
                    'temp_low' => 23,
                ],
                200,
                []
            ),
        ]);
        $this->get(route('get.rain-chance'))
            ->assertOk()
            ->assertJson(['Error' => 'Failed to get the data of rain chance.']);
    }
    public function testGetRainChanceCanWorkWhenGet500()
    {
        Http::fake([
            'https://weather.tw/taiwan' => Http::response(
                '',
                500,
                []
            ),
        ]);
        $this->get(route('get.rain-chance'))
            ->assertOk()
            ->assertJson(['Error' => 'Failed to get the data of rain chance.']);
    }
}
以上測試程式碼,測試了 3 種測試案例:
以上就是今天的介紹囉!
Mocking 的介紹到今天會告一段落,明後天讓我們來看看覆蓋率報告以及 phpunit.xml 吧!