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
相关产品推荐
相关产品推荐

