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() 則會套用共用中介層與共用選項。
將 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 還應驗證必要欄位與資料型別。
在服務提供者中註冊
在套件的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 的錯誤。
assertSent() 驗證的是至少有 1 筆符合的請求。為了不漏掉非預期的額外送出,也要搭配 assertSentCount()。assertSentInOrder() 會驗證筆數與順序,因此也可用於確認連續使用同一個用戶端時的狀態隔離。
防護的設定位於 parent::setUp() 之後。光靠這個測試無法驗證提供者啟動期間的通訊,因此請維持註冊與啟動階段不進行通訊的設計。
透過 fake 能確認的,是用戶端組裝的請求,以及針對預先準備之回應的處理。它無法保證實際服務的認證、TLS 連線、實際的逾時時間,乃至外部 API 的規格變更。需要外部連線的驗證,請在明確準備的驗證環境中另外進行。
長期維護時應追加的驗證
事先區分共用 Factory 與各個 PendingRequest 的職責,使用端就能直接沿用一般的 Laravel 測試方式。與其先打造自己的 fake 機制,不如先準備經由共用 Factory 的實作,以及確認這點的測試。
參考的第一手資料
- Laravel 官方文件:HTTP 用戶端
- Laravel 官方文件:套件開發
- Framework v13.39.0:Factory 的 fake、createPendingRequest、__call
- Framework v13.39.0:PendingRequest 的設定與 stub 處理
- Framework v13.39.0:Response 的 throw 與 JSON 解碼
- Framework v13.39.0:Http Facade 的解析目標與 fake
- Framework v13.39.0:FoundationServiceProvider 的 singleton 註冊
- Laravel 官方測試:HttpClientTest