5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Laravel11.x HttpファサードとFakeを使って外部サービスのモックテストをする

5
Last updated at Posted at 2024-07-05

なぜ外部APIをモックするのか

テストで外部APIを直接叩くと、相手側の都合(障害・メンテ・レート制限)でテストが不安定になる上、遅い・課金される・相手のサーバーに負荷をかけるといった問題があります。
そもそもテストで検証したいのは「外部APIが正しく動くか」ではなく、レスポンスを受け取った後の自分のコードのロジック。モックでレスポンスを固定すれば、検証対象を自分の責務だけに切り出せます。
ただしモックは自分で決めたレスポンスを返すだけなので、外部API側の仕様変更はモックでは検知できない。これは割り切って捨てているものです(線引きは記事末尾で)。

結論

Httpのファサードをテストしたい時は下記のようにする

被テスト側

HogeController.php
<?php

namespace App\Http\Controllers\Api\V1;

use Illuminate\Support\Facades\Http;

class HogeController extends Controller
{
    public function get() 
    {
        $response = Http::get('http://example.com/');
        return response()->json($response->json());
    }
}

→そのまま実行すると、404エラーになる
Routeは割愛

テスト側

※PestPHPで記載しています

HogeTest.php
<?php

use App\Http\Controllers\Api\V1\HogeController;
use Illuminate\Support\Facades\Http;

it('getテスト', function () {
    $response = [
        'message' => 'fake!'
    ];
    Http::fake([
        "http://example.com/" => Http::response($response, 200),
    ]);
    $controller = new HogeController();
    
    $response = $controller->get();

    expect(json_decode($response->getContent(), true))
        ->toEqual(
            [
                'message' => 'fake!'
            ]
        )
        ->and($response->status())
        ->toBe(200);
});

HogeTestを実行すると(./vendor/bin/pest --filter=HogeTest)
->message: fake!という文字列がレスポンスコード200で返却される

$responseの中身
array:1 [
  "message" => "fake!"
]

解説

通常は下記のように処理が進む

Route::get('/', 'HogeController@get');
-> HogeController::get()
    > Http::get('http://example.com(外部サービス)')
-> Http::get()した結果を返す

テスト時はFakeを使わないと下記のようになる

HogeTest
-> HogeController::get()
    > Http::get('http://example.com(外部サービス)')
-> Http::get()した結果を返す

問題点

  • http://example.comが実際にアクセス可能でないとテストできない
  • テストしたいデータがいつでも返ってくるようなサーバーでないと、再現できないテストケースが存在してしまう

改善

テスト時は下記のようにする

HogeTest
-> HogeController::get()
    > Http::fake()
-> Http::fake()の結果を返す

Http::fakeは特定URLに対して、特定のレスポンスをさせる便利なメソッド。
Httpはファサードであるため、その後にファサードを呼ぶクラスをインスタンス化し、実行することでfakeが有効になる

つまり、本番コードはそのままに、テスト時だけさまざまなレスポンスをテスト可能です!

モックで拾えないもの・異常系の設計

Http::fakeは正常系の固定だけでなく、外部が期待通りに返してくれない時の検証にこそ威力を発揮します。

異常系のfake例
Http::fake([
    // 5xx: 外部が落ちている時に自分のコードがどう振る舞うかを検証
    'http://example.com/' => Http::response([], 500),
    // 接続失敗(タイムアウト等): Laravel 11ならこれで再現できる
    'http://example.com/timeout' => Http::failedConnection(),
]);

予期しない形式のJSONを返すfakeを書けば、パース処理の防御も確認できます。外部が正常な時より「返してくれなかった時」の方が自分のコードの設計が問われるので、全網羅を目指す前に、まず「外部が落ちても自分のAPIが無言で500を返さない」ことを確認するテスト1本から始めるのがおすすめです。

一方で、外部API側が本当に仕様変更した場合の検知はモックテストの守備範囲外。そこは契約テストや本番の監視・アラートという別レイヤーの仕事です。「モックは自分のロジックの検証、仕様変更の検知は別の仕組み」と役割分担して使い分けましょう。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?