![]()
星期一早上,你打開專案,CI 顯示紅燈。
你從上週五之後就沒動過任何東西。打開 log:與支付 API 的整合測試失敗。Timeout。
他們的 gateway 在凌晨 3 點發生不穩定。你的程式碼完美無缺。即便如此,build 還是紅燈,部署卡住,現在你得花半小時證明問題不是出在你身上。
有遇過這種情況嗎?依賴外部 API 的測試就是這樣:速度慢、不穩定,而且因為不是你的原因而壞掉。還有更糟的情境 — 真的去打支付 API 的測試,每次執行 php artisan test 就會在 sandbox 實際發動 30 筆收款,直到 gateway 覺得可疑而封鎖你。
看看這個測試。看起來還算合理,對吧?
public function test_busca_o_cep(): void
{
// isso bate no ViaCEP de VERDADE, toda vez que o teste roda
$endereco = $this->service->buscarCep('01001000');
$this->assertEquals('São Paulo', $endereco['cidade']);
}
Enter fullscreen mode
Exit fullscreen mode
哪裡有問題?所有測試中重要的事都有問題:
測試應該要驗證的是你自己的程式碼,而不是其他人的伺服器是否健康。
Http::fake() 攔截一切現在來到這個故事的 $pedido->restore() 部分。如果你使用 Laravel 的 HTTP Client(Http::get()、Http::post()…),一行程式碼就能解決:
use IlluminateSupportFacadesHttp;
public function test_busca_o_cep(): void
{
// A partir daqui, NENHUMA requisição sai de verdade.
// Você diz o que cada URL deve responder.
Http::fake([
'viacep.com.br/*' => Http::response([
'localidade' => 'São Paulo',
'uf' => 'SP',
], 200),
]);
$endereco = $this->service->buscarCep('01001000');
$this->assertEquals('São Paulo', $endereco['cidade']);
}
Enter fullscreen mode
Exit fullscreen mode
搞定。 Http::fake() 會攔截請求並回傳你指定的回應,完全不碰網路。測試可以在離線狀態下以毫秒級速度執行,而且每次結果都相同。你現在主宰了 API 的行為。
fake() 非常有彈性。以下是你會經常使用到的幾種情境:
// Fingir QUALQUER requisição como 200 vazio (o mais simples)
Http::fake();
// Respostas diferentes por URL
Http::fake([
'github.com/*' => Http::response(['plano' => 'pro'], 200),
'viacep.com.br/*' => Http::response('', 404), // simula CEP não achado
]);
// Simular a API fora do ar (erro 500) — o caminho que ninguém testa
Http::fake([
'gateway.com/*' => Http::response('Erro interno', 500),
]);
Enter fullscreen mode
Exit fullscreen mode
注意最後一個:現在你可以測試失敗路徑了。「如果 gateway 回傳 500,我的應用程式是顯示友善訊息,還是直接在使用者面前崩潰?」如果沒有 fake(),你永遠無法回答這個問題。
這裡就是區分「真正測試」與「只是假裝」的陷阱所在。
模擬回應並不能保證你正確地組裝了請求。有沒有傳認證 header?有沒有把值放在正確的欄位?Http::assertSent() 可以檢查這些:
Http::fake();
$this->service->cobrar(valor: 150, cartao: 'tok_abc');
Http::assertSent(function (Request $request) {
return $request->url() === 'https://gateway.com/charges'
&& $request->hasHeader('Authorization')
&& $request['amount'] === 150;
});
Enter fullscreen mode
Exit fullscreen mode
現在測試同時保證了兩邊:你正確地送出了該送的內容,而且正確地回應了收到的結果。
有一個很陰險的細節。如果你忘記為某個 URL 設定 fake,請求就會真的發出去 — 而你根本不會發現那個測試仍然依賴網路。
preventStrayRequests() 可以把這扇門關起來:
Http::preventStrayRequests();
Http::fake([
'gateway.com/*' => Http::response('ok'),
]);
Http::get('https://gateway.com/charges'); // ok, tá fakeado
Http::get('https://outra-api.com'); // 💥 lança exceção na hora
Enter fullscreen mode
Exit fullscreen mode
任何沒有對應 fake 的請求都會立刻拋出例外,而不是悄悄地跑到網路上。把這行放到你測試套件的 setUp() 裡,就能安心睡覺了。
黃金法則:測試不應該和外面的世界對話。它應該快速且可預測地驗證你的程式碼。與 API 的真實對話,請留給獨立的整合測試,偶爾執行就好 — 而不是每次 commit 都跑。
一個重要細節:這只有在你使用 Laravel 的 HTTP Client(Http::)時才有效。如果你還是直接使用 Guzzle 或 cURL,fake() 就無法作用 — 而改用 Http:: 正是下一步(劇透:它也免費提供 timeout 和 retry,但那是另一篇文章的話題)。
告訴我:你在測試中會 mock API,還是仍然每天祈禱 sandbox 不要出問題?👀
https://dev.to/denisgusto1/seu-teste-bate-na-api-de-verdade-um-dia-ela-vai-te-trair-4l97