You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,
        ]);
    }
}

重要注意事项

  • 测试顺序影响结果:因为所有测试共享同一个数据库状态,所以测试方法的执行顺序会影响最终结果,最好保持测试的逻辑连贯性,比如先登录再登出。
  • 隔离独立测试:如果有不需要共享状态的测试,别放在这个类里,单独新建测试类使用默认的DatabaseTransactions trait。
  • 数据库引擎支持:确保你的测试数据库使用支持事务的引擎,比如MySQL的InnoDB,MyISAM不支持事务,这种方法会失效。

内容的提问来源于stack exchange,提问作者Lloyd Banks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:50:12