Laravel功能测试:如何仅调用一次外部HTTP请求?
问题解决:Laravel测试中仅调用一次外部API获取Bearer Token
可以实现只调用一次外部API,核心思路是用静态变量存储全局Token,在测试类的setUp方法中判断静态Token是否已存在,不存在则调用API获取,之后所有测试实例共享这个静态Token。
可行实现代码
<?php namespace Tests\Feature\Controllers; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\TestCase; class MyTest extends TestCase { // 静态变量存储全局Token,所有测试实例共享 protected static ?string $globalBearerToken = null; // 实例变量,每个测试方法可用 protected string $bearerToken; public function setUp(): void { parent::setUp(); // 仅当静态Token未初始化时,调用一次API if (!self::$globalBearerToken) { self::$globalBearerToken = self::getToken(); } // 将静态Token赋值给实例变量,确保每个测试方法都能访问 $this->bearerToken = self::$globalBearerToken; } protected static function getToken() { $response = Http::post('http://auth_api/oauth/token', [ // 你的认证参数 ]); // 这里可以保留错误处理,避免测试因API失败崩溃 if ($response->failed()) { throw new \RuntimeException('获取认证Token失败'); } return $response->json()['access_token']; } // 你的测试方法示例 public function test_protected_endpoint() { $this->withHeaders([ 'Authorization' => 'Bearer ' . $this->bearerToken, ])->get('/api/protected') ->assertStatus(200); } }
为什么之前的尝试出错?
手动标记静态变量的方案:
你之前只在第一个测试实例中给$bearerToken赋值,但后续测试实例的$bearerToken是未初始化的(因为是实例变量,每个测试都会创建新的类实例),所以会报$bearerToken must not be accessed before initialization。上面的方案通过静态变量存储Token,再在每个setUp中把静态Token赋值给实例变量,解决了这个问题。setUpBeforeClass方案:
PHPUnit的setUpBeforeClass是在Laravel应用启动前执行的,此时Laravel的Facade(比如Http)还没有被初始化,所以会报A facade root has not been set。而setUp方法是在Laravel应用启动后执行的,所以可以正常使用HttpFacade。
其他可选方案
1. 跨测试类共享Token(全局测试套件)
如果希望整个测试套件(多个测试类)共享同一个Token,可以在tests/bootstrap.php中添加初始化逻辑:
// tests/bootstrap.php use Illuminate\Support\Facades\Http; // 确保Laravel应用初始化 $app = require __DIR__.'/../bootstrap/app.php'; $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); // 获取并存储Token到全局变量 global $globalTestToken; $globalTestToken = Http::post('http://auth_api/oauth/token', [ // 认证参数 ])->json()['access_token'];
然后在测试类中直接使用:
public function setUp(): void { parent::setUp(); global $globalTestToken; $this->bearerToken = $globalTestToken; }
2. 手动初始化Laravel的setUpBeforeClass方案
如果一定要用setUpBeforeClass,需要手动初始化Laravel应用:
public static function setUpBeforeClass(): void { // 初始化Laravel应用 $app = require __DIR__.'/../../../bootstrap/app.php'; $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap(); self::$globalBearerToken = self::getToken(); }
不过这种方式不如setUp中判断静态变量的方案优雅,因为会提前初始化Laravel,可能影响其他测试的隔离性。
内容的提问来源于stack exchange,提问作者nwantiti
相关产品推荐
相关产品推荐

