Http::fake()仅拦截Illuminate\Support\Facades\Http门面请求,不拦截GuzzleClient实例、curl_init()等;需统一用Http门面或注入可mock客户端;匹配URL须含协议域名;伪造4xx/5xx需显式调用->throw();测试后应重置fake避免污染。

Http::fake 会拦截所有 Http::get/post 等调用,但不拦截 GuzzleClient 实例
这是最容易踩的坑:Laravel 的 Http::fake() 只作用于 Illuminate\Support\Facades\Http 门面发起的请求,对直接 new GuzzleHttp\Client() 或其他 HTTP 客户端完全无效。如果你在测试中用了自定义客户端,Http::fake() 不会起作用。
常见错误场景:
- 服务类里写了
$client = new GuzzleHttp\Client(),然后调用$client->get() - 第三方包内部硬编码了 Guzzle 实例
- 用了
curl_init()或file_get_contents()
解决办法只有两个:统一改用 Http:: 门面,或在测试中手动替换依赖(比如通过构造函数注入可 mock 的 client 接口)。
伪造不同 URL 返回不同响应,要用数组键匹配完整 URL 或正则
Http::fake() 接收一个关联数组,键是 URL 匹配规则,值是响应配置。键支持三种形式:
- 完整 URL 字符串(如
'https://api.example.com/users') - 带通配符的字符串(如
'https://api.example.com/*') -
正则表达式(需用
PREG_PATTERN_ORDER语法,如'~^https://api\.example\.com/v\d+/users$~')
注意:URL 必须包含协议和域名,'/users' 这种路径写法不会匹配任何请求。
Http::fake([
'https://api.example.com/users' => Http::response(['id' => 1, 'name' => 'Alice'], 200),
'https://api.example.com/posts/*' => Http::response(['data' => []], 200),
'~^https://api\.example\.com/v2/.*~' => Http::response(['error' => 'Deprecated'], 410),
]);
伪造失败响应(4xx/5xx)时,记得调用 response()->throw() 才会抛异常
默认情况下,Http::response($body, $status) 返回的响应对象不会自动抛出异常,即使状态码是 404 或 500。只有当你显式调用 ->throw(),Laravel 才会把非 2xx 响应转为 GuzzleHttp\Exception\ServerException 或 ClientException。
所以,如果业务代码里写了:
$response = Http::get('https://api.example.com/user/999');
$data = $response->throw()->json(); // 这里才会触发异常
那么测试中伪造 404 就必须确保调用了 throw():
Http::fake([
'https://api.example.com/user/999' => Http::response(['message' => 'Not Found'], 404),
]);
// 测试断言
$this->expectException(GuzzleHttp\Exception\ClientException::class);
Http::get('https://api.example.com/user/999')->throw();
测试结束后要清理 fake,否则会影响后续测试用例
Http::fake() 是全局状态,一旦调用就会持续生效,直到被覆盖或重置。如果你在某个 test 方法里调用了它,而没做清理,后面其他 test 方法里的 HTTP 请求也会被拦截——哪怕它们没打算 fake。
推荐做法是在 setUp() 或每个 test 开头调用 Http::fake(),并在 tearDown() 中调用 Http::preventStrayRequests() 或直接重置:
// 在 test 方法开头重置更稳妥
public function test_something()
{
Http::fake(); // 清掉之前所有 fake 规则,只拦截不响应(即报错)
Http::fake([
'https://api.example.com/data' => Http::response(['ok' => true]),
]);
// ... 测试逻辑
}
更严格的做法是加 Http::preventStrayRequests(),这样任何未被 fake 的请求都会直接失败,避免漏测真实网络调用。
真正麻烦的不是怎么 fake,而是 fake 后忘记还原,导致测试间相互污染——这个细节在 CI 环境里特别难排查。










