Laravel 10单元测试无法识别Artisan命令的数据库变更
Laravel测试中Artisan命令数据库变更未被识别的解决方法
问题场景
在Laravel 10中实现user:promote Artisan命令,用于将用户升级为超级管理员。命令执行后通过输出确认数据库已更新,但运行单元测试时,断言无法识别到该变更,其他非Artisan相关的94+个数据库测试均正常。
核心原因
Laravel测试默认以事务模式运行:测试开始时开启数据库事务,测试结束后自动回滚。但Artisan命令执行时会创建一个独立的数据库连接实例,这个连接脱离了测试的事务上下文——命令的变更会直接提交到数据库,但测试代码仍在原事务中查询,因此无法看到新的修改。
即使在命令中用dd($user)能看到变更,也只是因为该输出是基于命令自身的数据库连接,和测试的连接不是同一个上下文。
解决方案
方案1:刷新测试中的用户模型(最简单)
执行命令后,通过refresh()方法让用户模型重新从数据库拉取最新数据,确保测试使用的是数据库中的实际值:
public function test_promote_user_command_sets_user_as_super_admin() { $user = $this->createUser('guest'); $this->artisan("user:promote {$user->email}")->assertSuccessful(); // 刷新模型,获取数据库中的最新数据 $user->refresh(); $role = Role::where('name', '=', 'Super Admin')->first(); $this->assertEquals($role->id, $user->role_id); // 或者继续使用assertDatabaseHas $this->assertDatabaseHas('users', [ 'id' => $user->id, 'role_id' => $role->id ]); }
方案2:关闭测试事务,改用全量迁移
如果测试集不大,可以关闭事务模式,每次测试都重新迁移数据库,确保测试和命令使用同一个数据库状态:
use Illuminate\Foundation\Testing\RefreshDatabase; class UserPromoteTest extends TestCase { use RefreshDatabase; protected function setUp(): void { parent::setUp(); // 强制每次测试重新执行迁移,替代事务回滚 $this->refreshDatabase(); } public function test_promote_user_command_sets_user_as_super_admin() { $user = $this->createUser('guest'); $this->artisan("user:promote {$user->email}")->assertSuccessful(); $role = Role::where('name', '=', 'Super Admin')->first(); $this->assertDatabaseHas('users', [ 'id' => $user->id, 'role_id' => $role->id ]); } }
注意:此方法会增加测试执行时间,适合小型测试套件。
方案3:让命令使用测试的数据库连接
修改Artisan命令,强制使用测试当前的数据库连接,确保命令和测试处于同一个事务上下文:
class PromoteUserCommand extends Command { protected $signature = 'user:promote {email}'; protected $description = 'Promote a user to super admin.'; public function handle(): void { // 使用测试的默认数据库连接 $connection = $this->laravel['db']->getDefaultConnection(); $role = Role::on($connection) ->where('name', '=', 'Super Admin') ->first(); $user = User::on($connection) ->where('email', '=', $this->argument('email')) ->first(); $user->role_id = $role->id; $user->save(); } }
这样命令的所有数据库操作都会在测试的事务中执行,测试断言就能直接识别到变更。
内容的提问来源于stack exchange,提问作者JLZenor
相关产品推荐
相关产品推荐

