Laravel 5 PHPUnit测试:将控制器所有测试包裹进单个事务
如何在Laravel中为整个测试类共享单个数据库事务
嘿,这个场景我太熟悉了!默认的DatabaseTransactions trait确实是为每个独立测试方法单独创建并回滚事务,所以跨测试的数据库数据(比如你说的登录token)根本留不住。要实现整个测试类共用一个事务,咱们可以手动接管事务的生命周期,不用那个默认trait就行,步骤如下:
1. 移除默认的事务Trait
首先把测试类里的use Illuminate\Foundation\Testing\DatabaseTransactions;删掉,它的自动事务处理会和咱们的全局事务冲突。
2. 手动控制全局事务的开启与回滚
利用PHPUnit的静态生命周期方法,在所有测试执行前开启事务,全部测试跑完后再回滚:
use Illuminate\Support\Facades\DB; use Tests\TestCase; class AuthFlowTest extends TestCase { protected static $globalTransaction; // 所有测试方法执行前触发 public static function setUpBeforeClass(): void { parent::setUpBeforeClass(); // 开启全局数据库事务 static::$globalTransaction = DB::beginTransaction(); } // 所有测试方法执行完后触发 public static function tearDownAfterClass(): void { // 回滚全局事务,还原数据库状态 DB::rollBack(); parent::tearDownAfterClass(); }
3. 禁用测试方法的自动事务回滚
Laravel的测试框架默认会在每个测试方法结束时尝试回滚事务,咱们得把这个行为关掉,避免干扰全局事务:
protected function setUp(): void { parent::setUp(); // 覆盖默认的事务回滚逻辑 $this->app->instance('Illuminate\Contracts\Testing\TestDatabaseState', new class { public function shouldRollBack(): bool { return false; } }); }
4. 编写你的测试方法
现在整个测试类里的所有测试都会共享同一个事务上下文了,登录生成的token会一直存在到整个测试类跑完:
public function test_login_creates_valid_token() { // 模拟登录请求 $response = $this->post('/api/login', [ 'email' => 'test@user.com', 'password' => 'testpass123', ]); $response->assertStatus(200); // 验证token已经存入数据库 $this->assertDatabaseHas('personal_access_tokens', [ 'tokenable_id' => 1, // 假设测试用户ID为1 ]); } public function test_logout_revokes_existing_token() { // 直接从数据库获取上一个测试生成的token $user = \App\Models\User::find(1); $token = $user->tokens()->latest()->first(); // 带着token发起登出请求 $response = $this->withHeaders([ 'Authorization' => "Bearer {$token->plainTextToken}", ])->post('/api/logout'); $response->assertStatus(200); // 验证token已被吊销 $this->assertDatabaseHas('personal_access_tokens', [ 'id' => $token->id, 'revoked' => true, ]); } }
重要注意事项
- 测试顺序影响结果:因为所有测试共享同一个数据库状态,所以测试方法的执行顺序会影响最终结果,最好保持测试的逻辑连贯性,比如先登录再登出。
- 隔离独立测试:如果有不需要共享状态的测试,别放在这个类里,单独新建测试类使用默认的
DatabaseTransactionstrait。 - 数据库引擎支持:确保你的测试数据库使用支持事务的引擎,比如MySQL的InnoDB,MyISAM不支持事务,这种方法会失效。
内容的提问来源于stack exchange,提问作者Lloyd Banks
相关产品推荐
相关产品推荐

