Symfony 3.4下如何在SQLite功能测试中启用外键检查
我太懂这种踩坑的感觉了——生产环境MySQL因为外键约束拦着删不了父级,结果测试用SQLite因为默认没开外键检查,完全测不出这个bug,等到上线才炸,真的头疼!
下面给你几个靠谱的解决方法,确保控制器使用的连接能正确开启外键检查:
方法1:全局配置(最省心)
直接在Rails的config/database.yml的test环境里添加外键开启配置,这样所有ActiveRecord连接(包括控制器用的)都会默认启用外键检查:
test: adapter: sqlite3 database: db/test.sqlite3 pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> timeout: 5000 variables: foreign_keys: 'ON'
这个方法一劳永逸,不用在测试里写额外代码,重启测试服务后就会生效。
方法2:测试框架钩子(动态控制)
如果全局配置不适用(比如有些测试需要临时关闭外键),可以在测试的setup阶段给当前连接手动开启外键:
针对Minitest
在test/test_helper.rb里添加全局setup钩子:
class ActiveSupport::TestCase # 其他配置... setup do # 给当前ActiveRecord连接开启外键检查 ActiveRecord::Base.connection.execute("PRAGMA foreign_keys = ON") end end
针对RSpec
在spec/rails_helper.rb里添加before钩子:
RSpec.configure do |config| # 其他配置... config.before(:each) do ActiveRecord::Base.connection.execute("PRAGMA foreign_keys = ON") end end
这里的ActiveRecord::Base.connection就是控制器操作时用的连接,和fixtures加载时的临时连接不是同一个,所以这样设置才能覆盖到你需要的场景。
验证是否生效
可以在测试里加个调试语句确认外键状态:
test "外键检查已开启" do status = ActiveRecord::Base.connection.execute("PRAGMA foreign_keys").first[0] assert_equal 1, status, "外键检查未成功开启" end
返回1表示外键检查已启用,0则未启用。
为什么fixtures里执行没用?
因为fixtures是在测试启动前单独加载的,用的是一个独立的临时连接,而控制器的数据库操作是在测试运行时使用ActiveRecord的主连接,所以你之前在fixtures里执行的PRAGMA根本影响不到控制器用的连接,这就是问题所在。
现在设置好之后,你再测试删除有子级的父级时,SQLite就会像生产环境的MySQL一样拒绝删除,帮你提前发现这个bug啦!
内容的提问来源于stack exchange,提问作者Topper Harley

