如何编写RSpec测试模拟RoR5应用MySQL连接断开场景
测试Rails 5线程场景下MySQL断开后的连接状态
嘿,我来帮你搞定这个RSpec测试的事儿,之前我也碰到过Rails线程+数据库连接池的坑,正好能给你捋清楚步骤。核心思路就是模拟MySQL连接断开的场景,验证线程里的ActiveRecord操作是否会陷入无法恢复的错误状态,同时让RSpec能直接操控应用的数据库连接池。
第一步:配置RSpec,确保能操控应用的DB连接
首先得让测试环境和应用运行时用同一个连接池,而且测试前后要清理,避免污染其他测试。在rails_helper.rb里加这段配置:
RSpec.configure do |config| config.before(:suite) do # 初始化测试用的连接池,和应用环境保持一致 ActiveRecord::Base.connection_pool.disconnect! ActiveRecord::Base.establish_connection(Rails.env.to_sym) end config.after(:each) do # 每次测试后重置连接池,防止残留的失效连接影响后续测试 ActiveRecord::Base.connection_pool.disconnect! ActiveRecord::Base.establish_connection(Rails.env.to_sym) end end
第二步:模拟MySQL连接断开的场景
我们需要手动让应用的现有数据库连接失效,模拟生产环境中MySQL重启的情况。写个辅助方法来干这个:
def simulate_db_disconnect # 断开连接池里所有活跃连接 ActiveRecord::Base.connection_pool.disconnect! # 到这一步,应用里的旧连接就都失效了,和MySQL重启后的状态一致 end
如果想要更真实的模拟(比如真的重启MySQL),你可以在测试里调用系统命令重启测试环境的MySQL服务,但这需要测试环境有足够权限,而且可能影响其他测试,所以建议只在单独的测试套件里用。
第三步:编写具体的测试用例
接下来模拟你rails runner里的线程逻辑,验证断开后的表现:
require 'rails_helper' RSpec.describe 'MySQL disconnection resilience with threads', type: :system do it 'enters un-recoverable connection state after MySQL disconnect (as per production issue)' do # 启动一个长期运行的线程,模拟runner里的业务逻辑 worker_thread = Thread.new do loop do begin # 用你实际的模型操作来模拟线程里的DB访问 User.last sleep 0.2 rescue ActiveRecord::ConnectionNotEstablished => err # 记录错误,让线程退出(和你应用的实际逻辑对应) Thread.current[:connection_error] = err break end end end # 等线程启动并完成一次DB操作,确保它已经从连接池拿到了连接 sleep 0.5 # 模拟MySQL断开 simulate_db_disconnect # 等线程尝试再次执行DB操作,触发错误 sleep 1 # 验证线程是否捕获到了连接错误(对应生产环境的报错) expect(worker_thread[:connection_error]).to be_a(ActiveRecord::ConnectionNotEstablished) # 验证线程是否已经停止运行(如果你的应用线程没有重连逻辑的话) expect(worker_thread.alive?).to be false # 额外验证:主线程重新建立连接后,是否能正常访问DB ActiveRecord::Base.establish_connection(Rails.env.to_sym) expect { User.first }.not_to raise_error # 但之前的工作线程因为持有失效连接,已经无法恢复,这就是生产环境的问题所在 end end
关键注意点
- 线程连接的生命周期:Rails的连接池是线程安全的,但每个线程会从池里获取一个连接并持有。如果连接失效后,线程没有主动重新从池里拿新连接,就会一直用失效的连接,导致持续报错——这正是你生产环境的问题,测试里要重点验证这一点。
- 测试隔离:一定要在测试前后重置连接池,不然失效的连接会污染其他测试用例。
- 贴合实际业务:把测试里的
User.last换成你runner脚本里实际的DB操作,这样测试场景才和生产一致。
内容的提问来源于stack exchange,提问作者user2452057
相关产品推荐
相关产品推荐

