通过为每位测试用户动态创建独立克隆数据库,并在会话结束时自动清理,可确保所有数据变更完全隔离且不留痕迹,真正实现“用完即焚”的测试体验。
在 Laravel 中实现“测试账户操作后自动回滚至初始状态”,不能依赖常规事务(如 DB::beginTransaction())——因为 HTTP 请求间无共享事务上下文,且 Eloquent 操
作分散在多个请求中(如编辑、删除、提交表单),DB::rollback() 无法跨请求生效,更无法影响其他请求中已提交的事务。
✅ 正确思路:为每个测试用户分配专属、临时、隔离的数据库实例,而非共享同一数据库。
// app/Http/Controllers/Auth/LoginController.php
protected function authenticated(Request $request, $user)
{
// 仅对测试账号启用此逻辑(建议加角色/邮箱白名单校验)
if ($user->isTestAccount()) {
$dbCloneName = 'test_' . Str::slug(session()->getId());
// 克隆数据库(需确保 MySQL 用户有 CREATE/GRANT 权限)
$command = "mysqldump test_template | mysql {$dbCloneName}";
exec($command, $output, $returnCode);
if ($returnCode !== 0) {
throw new RuntimeException("Failed to clone database: " . implode("\n", $output));
}
// 切换当前连接到新克隆库
Config::set('database.connections.mysql.database', $dbCloneName);
session(['test_db_name' => $dbCloneName]);
}
}// app/Http/Controllers/Auth/LoginController.php
public function logout(Request $request)
{
$dbCloneName = session('test_db_name');
if ($dbCloneName && Str::startsWith($dbCloneName, 'test_')) {
DB::statement("DROP DATABASE IF EXISTS `{$dbCloneName}`");
session()->forget('test_db_name');
}
$this->guard()->logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return redirect('/');
}CREATE USER 'cloner'@'localhost' IDENTIFIED BY 'strong-pass'; GRANT SELECT ON test_template.* TO 'cloner'@'localhost'; GRANT CREATE, DROP ON `test_%`.* TO 'cloner'@'localhost'; FLUSH PRIVILEGES;
? 总结:事务无法跨越 HTTP 请求生命周期,而数据库克隆从根源上实现了数据沙箱化。它虽增加少量初始化开销,却换来 100% 可靠的测试环境一致性——这才是面向公共测试应用的稳健实践。