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

Laravel 8.33.1并行测试时MySQL数据库出现‘已存在活跃事务’PDOException错误求助

解决Laravel并行测试中的事务冲突与数据初始化问题

我来帮你分析下你遇到的这两个错误,然后给出具体的修复方案:

问题根源分析

1. 事务冲突错误 (There is already an active transaction)

这个问题出在并行测试的数据库初始化流程和DatabaseTransactions trait的交互上:

  • 当你使用--recreate-databases参数时,Laravel会为每个测试进程创建独立的测试数据库,然后运行setUpTestDatabase闭包执行seed。
  • 你的迁移文件中使用了DB::unprepared执行SQL dump,如果dump文件里包含未正确提交的事务(比如遗留的BEGIN语句),或者Artisan::call('db:seed')执行过程中触发了事务,而DatabaseTransactions又会在每个测试开始前自动开启新事务,就会导致事务冲突。

2. company_id非空约束错误

这个错误说明测试中创建User时,company_id字段没有被正确赋值:

  • 要么你的User Factory没有关联Company模型,无法自动生成有效的company_id;
  • 要么并行测试的seed没有正确执行,数据库中没有预先创建的Company数据,导致Factory无法获取有效值。

具体修复方案

方案1:修复事务冲突问题

修改AppServiceProvider.php中的setUpTestDatabase闭包,确保初始化seed时没有活跃事务:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\ParallelTesting;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        ParallelTesting::setUpTestDatabase(function () {
            // 先检查并回滚可能存在的活跃事务
            if (DB::connection()->getPdo()->inTransaction()) {
                DB::rollBack();
            }

            // 在独立事务中运行seed,避免影响后续测试的事务
            DB::transaction(function () {
                Artisan::call('db:seed');
            });
        });
    }
}

另外,务必检查你的dump.sql文件,确保所有事务都正确提交(没有遗漏的COMMIT或未关闭的BEGIN),可以手动执行该SQL文件验证是否能正常运行。

方案2:修复company_id非空约束问题

调整User Factory

确保User Factory自动关联Company模型,这样创建User时会自动生成关联的Company:

// database/factories/UserFactory.php
<?php

namespace Database\Factories;

use App\Models\Company;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;

class UserFactory extends Factory
{
    protected $model = User::class;

    public function definition()
    {
        return [
            'company_id' => Company::factory(), // 自动创建并关联Company
            'email' => $this->faker->unique()->safeEmail(),
            'password' => Hash::make('test_1'),
            // 其他必填字段...
        ];
    }
}

调整Seeder执行顺序

如果需要预先创建批量Company数据,确保DatabaseSeeder先执行CompanySeeder:

// database/seeders/DatabaseSeeder.php
<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        // 先创建Company数据,再执行其他Seeder
        $this->call([
            CompanySeeder::class,
            // 其他Seeder...
        ]);
    }
}

方案3:验证并行测试流程

你可以先手动验证初始化流程是否正常:

# 创建一个测试数据库副本
mysql -u [你的用户名] -p -e "CREATE DATABASE [你的应用名]_test_1;"

# 运行迁移和seed
php artisan migrate:fresh --seed --env=testing

# 运行并行测试(不重建数据库)
php artisan test --parallel

如果这个流程能正常运行,说明你的初始化逻辑没问题,问题确实出在--recreate-databases时的事务冲突,方案1的修复应该能解决。


按照以上步骤调整后,应该能解决你遇到的两个错误,顺利运行并行测试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 11:04:09