メインコンテンツへスキップ
パッケージから外部APIを利用したいときは、Laravelが管理するHTTPクライアントの設定を引き継ぐことと、呼び出しごとの設定を持ち越さないことを両立させます。独自にFactoryを生成したり、送信前のリクエストを長期間保持したりすると、利用側のfakeが適用されない原因になります。 このページはLaravel Framework v13.39.0 の実装を確認しています。基本操作はHTTPクライアント、検証環境の準備はパッケージのテストを参照してください。

Factoryは共有し、PendingRequestは毎回作る

LaravelのHTTPクライアントには、異なる責任を持つオブジェクトがあります。 FoundationServiceProvider はFactoryをsingletonとして登録し、Http ファサードも同じクラス名を使ってコンテナから解決します。そのため、通常のLaravelアプリケーションでは、コンテナから受け取ったFactoryに対する通信も Http::fake() で検証できます。ファサードを直接使う設計も可能ですが、このページでは依存関係を明示するためFactoryを注入します。 Factoryに対する baseUrl() などの通常のリクエスト設定メソッドは、__call() から新しいPendingRequestへ転送されます。createPendingRequest() は生成時点のfake用コールバックとstray requestの制限を渡し、newPendingRequest() は共通ミドルウェアと共通オプションを適用します。
パッケージ内で new Factory() や別のHTTPライブラリのクライアントを生成しても、共有Factoryのfakeや Http::preventStrayRequests() は引き継がれません。これらはプロセス全体の通信を遮断する機能ではありません。Laravel向けクライアントでは共有Factoryを使い、独立したクライアントを提供する場合は、そのクライアント専用のテスト方法を用意してください。

API操作を小さなクライアントにまとめる

架空のカタログAPIから商品を取得する Acme\Catalog パッケージを例にします。src/CatalogClient.php はFactoryとベースURLだけを保持し、リクエストは product() の実行中に作ります。
この設計では、同じクライアントで product(42, 'ja') の後に product(42) を呼んでも、前の呼び出しで追加した Accept-Language は残りません。利用側アプリケーションがFactoryに設定した共通設定は、引き続き各リクエストへ適用されます。
  • パッケージ固有の設定は、新しく作ったPendingRequestへ適用します。パッケージの初期化で Http::globalOptions() を設定すると、無関係な通信にも影響するため避けます。
  • コンストラクターでは送信しません。サービスの解決、Artisanの起動、設定ファイルの公開だけで外部APIへ接続しない設計にします。
  • throw() は4xx・5xxを RequestException にします。接続失敗やタイムアウトの ConnectionException と区別し、失敗を空配列などの成功値に置き換えません。
  • json(flags: JSON_THROW_ON_ERROR) は不正なJSONを JsonException にします。例では配列かどうかだけを検査しているため、実際のAPIでは必須フィールドやデータ型も検証します。
ベースURLやヘッダーの共通化だけなら、公式ドキュメントのHTTPマクロも選択肢です。API操作、戻り値の検査、例外の契約までパッケージとして提供する場合は、このような専用クラスに分けると保守しやすくなります。マクロの仕組みはMacroableトレイトを参照してください。

サービスプロバイダーで登録する

パッケージの config/catalog.php に初期設定を用意します。ここにHTTPクライアントやクロージャを保存せず、設定キャッシュへ格納できる値だけを置きます。
src/CatalogServiceProvider.php は設定をマージし、クライアントを解決するときに共有Factoryを渡します。Factoryそのもののバインディングは上書きしません。
プロバイダーはパッケージの自動検出などで読み込みます。利用者は設定を公開し、config/acme-catalog.php のベースURLを実際のAPIへ変更します。例の catalog.example.test は説明とfake用の架空のホストです。
利用側ではコンストラクター注入やコンテナでクライアントを解決します。この呼び出しで初めて商品取得のリクエストが送信されます。
登録には bind() を使っていますが、クライアントを保持する側がsingletonなら、その参照まで毎回作り直されるわけではありません。呼び出し固有の言語、認証情報、リクエストオブジェクトをプロパティに保存しないことが重要です。詳細はパッケージサービスのライフサイクル設計を参照してください。 設定の上書きとデプロイ時のキャッシュ更新はパッケージ設定のマージとキャッシュで扱っています。ベースURLは信頼できるアプリケーション設定として管理し、リクエストの入力から接続先を自由に指定させないでください。

本物のクライアントをfakeで検証する

クライアント自体をモックすると、共有Factoryを利用しているか、URLやヘッダーを正しく組み立てているかは確認できません。パッケージのテストでは、本物の CatalogClient をコンテナから解決し、HTTP応答だけをfakeにします。 次はOrchestra Testbenchのテスト環境で実行する例です。各テストの setUp() で preventStrayRequests() を有効にします。通信するテストでは想定URLにだけ応答を設定し、'*' のフォールバックを置かないことで、URLの誤りも検出できます。
2番目のテストでは、クライアントを解決してからfakeを設定しています。Factoryを保持し、実際の呼び出し時にPendingRequestを作るため、この順番でもfakeが反映されます。コンストラクターでPendingRequestを生成・保持する設計へ変更すると、生成後に追加したfakeが反映されないことを検出できます。 assertSent() は一致するリクエストが1件以上あることの検証です。意図しない追加送信を見逃さないよう assertSentCount() も組み合わせます。assertSentInOrder() は件数と順序を検証するため、同じクライアントを続けて使ったときの状態分離にも利用できます。 ガードの設定は parent::setUp() の後です。このテストだけでプロバイダー起動中の通信まで検証できるわけではないため、登録と起動では通信しない設計を維持します。
fakeで確認できるのは、クライアントが組み立てたリクエストと、用意した応答に対する処理です。実サービスの認証、TLS接続、実際のタイムアウト時間、外部APIの仕様変更まで保証するものではありません。外部接続を伴う検証は、明示的に準備した検証環境で別に実施します。

長期保守で追加する検証

共有Factoryと各PendingRequestの責任を分けておくと、利用側が通常のLaravelのテスト方法をそのまま使えます。独自のfake機構を先に作るよりも、まず共有Factoryを通る実装と、それを確認するテストを用意してください。

参照した一次情報

最終更新日 2026年10月27日