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

Ruby on Rails教程第11章测试报错:SQLite3数据库锁定异常

Rails教程第11章测试SQLite数据库锁问题

问题现象

运行AccountActivationTest测试类时,只要包含以下测试用例:

test 'should not be able to log in with invalid email' do
  get edit_account_activation_path(@user.activation_token, email: 'wrong')
  assert_not is_logged_in?
end

就会批量触发错误:

Minitest::UnexpectedError:ActiveRecord::StatementInvalid: SQLite3::BusyException: database is locked
  • 单独运行该测试用例时无报错
  • 移除该用例后所有测试正常通过
  • 仅多个测试组合运行时出现锁冲突

原因分析

SQLite是单文件数据库,同一时间仅允许一个写操作。测试运行时,多个用例的数据库操作(尤其是写操作)可能因事务未及时释放、连续请求处理等原因抢锁,导致数据库锁死。无效邮箱的测试用例可能在请求流程中触发了意外的写操作(如会话记录、错误日志),和其他测试的写操作冲突。

解决方案

1. 优化测试类继承结构

若UsersSignup父类的事务处理存在异常,可改为直接继承集成测试基类,并迁移setup逻辑:

class AccountActivationTest < ActionDispatch::IntegrationTest
  def setup
    post users_path, params: { user: {
      name: 'Example User',
      email: 'user@example.com',
      password: 'password',
      password_confirmation: 'password'
    }}
    @user = assigns(:user)
  end

  # 原有测试用例保持不变
end

2. 调整测试运行顺序

在test/test_helper.rb中设置固定运行顺序,减少随机顺序导致的锁冲突:

Minitest::Test.run_order = :sorted

3. 修改SQLite测试配置

在config/database.yml的test环境中关闭同步,缩短锁持有时间:

test:
  adapter: sqlite3
  database: db/test.sqlite3
  synchronous: 0

4. 测试结束后强制关闭数据库连接

在测试类中添加teardown方法,确保每个测试释放连接:

def teardown
  super
  ActiveRecord::Base.connection.close
end

5. 优化激活控制器逻辑

确保无效邮箱请求不触发写操作,先验证邮箱有效性再执行后续逻辑:

class AccountActivationsController < ApplicationController
  def edit
    user = User.find_by(email: params[:email])
    if user && !user.activated? && user.authenticated?(:activation, params[:id])
      user.activate
      log_in user
      redirect_to user
    else
      flash[:danger] = "Invalid activation link"
      redirect_to root_url
    end
  end
end

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 22:48:20