跳轉到主要內容
想在套件中使用外部 API 時,需要同時做到沿用 Laravel 所管理的 HTTP 用戶端設定,以及不把每次呼叫的設定帶到下一次。若自行建立 Factory,或長時間保留尚未送出的請求,就會導致使用端的 fake 無法套用。 本頁確認了 Laravel Framework v13.39.0 的實作。基本操作請參閱HTTP 用戶端,測試環境的準備請參閱套件的測試。

共用 Factory,每次都建立新的 PendingRequest

Laravel 的 HTTP 用戶端由幾個負責不同職責的物件組成。 FoundationServiceProvider 會將 Factory 註冊為 singleton,Http Facade 也使用相同的類別名稱從容器解析。因此在一般的 Laravel 應用程式中,透過從容器取得的 Factory 所發出的通訊,也能以 Http::fake() 驗證。雖然也可以設計成直接使用 Facade,但本頁為了明確表示相依關係,改為注入 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 Trait。

在服務提供者中註冊

在套件的 config/catalog.php 中準備預設設定。這裡不要保存 HTTP 用戶端或閉包,只放可存入設定快取的值。
src/CatalogServiceProvider.php 會合併設定,並在解析用戶端時傳入共用 Factory。不會覆寫 Factory 本身的綁定。
提供者可透過套件自動探索等方式載入。使用者發布設定後,將 config/acme-catalog.php 的基礎 URL 改為實際的 API。範例中的 catalog.example.test 是用於說明與 fake 的虛構主機。
使用端透過建構子注入或容器來解析用戶端。直到這次呼叫,才會首次送出取得商品的請求。
註冊時使用了 bind(),但若持有用戶端的一方是 singleton,該參照並不會每次都重新建立。重點在於不要把該次呼叫專屬的語言、認證資訊或請求物件保存在屬性中。詳情請參閱套件服務的生命週期設計。 設定的覆寫與部署時的快取更新,請參閱套件設定的合併與快取。基礎 URL 應作為可信任的應用程式設定來管理,不要讓請求的輸入能任意指定連線目的地。

以 fake 驗證真正的用戶端

若直接 mock 用戶端本身,就無法確認它是否使用共用 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日