Rspec调用reload时清除before(:each)中更新的值,如何解决?
问题原因分析
你遇到的reload后字段变为nil的问题,核心原因有两类:
update_column方法调用错误:update_column接收两个位置参数,第一个是字段名符号,第二个是要更新的值,你传入name:"my new book"哈希参数的写法不符合方法要求,会导致更新逻辑异常,数据库实际没有写入预期的my second book值,部分版本Rails甚至会直接将对应字段写入nil。此时内存中的book2对象属性被临时赋值,但数据库里的值还是nil,调用reload会从数据库重新拉取数据覆盖内存属性,就会出现nil的结果。- 测试事务配置异常:如果你的RSpec开启了事务测试,且
rename_book方法内部触发了独立事务的提交/回滚,或者数据库清理器(DatabaseCleaner)的策略配置错误,也可能导致before块写入的数据被提前清理,reload时读到nil值。
直接修复方案
首先修正update_column的调用语法:
before(:each) do # 正确的update_column写法,两个位置参数 book.update_column(:name, "my new book") book2.update_column(:name, "my second book") end
如果需要同时更新多个字段,也可以用支持哈希传参的update_columns方法:
before(:each) do book.update_columns(name: "my new book") book2.update_columns(name: "my second book") end
修正后再执行测试,reload就会读到数据库里正确写入的字段值。如果问题依然存在,可以在rename_book调用前后打印数据库中book2的实际值,排查是否是rename_book方法内部误修改了book2的数据。
同类需求替代实现方案
如果不想在before块里做数据更新,也可以选择以下更简洁的实现方式:
- 直接在工厂创建时指定字段值,省去额外的更新步骤:
let!(:book){FactoryGirl.create(:book, name: "my new book")} let!(:book2){FactoryGirl.create(:book, name: "my second book")}
- 如果这类命名的测试用例较多,可以在工厂中定义特质(trait)复用:
# 工厂定义文件 FactoryGirl.define do factory :book do # 其他默认字段定义 trait :named_new do name { "my new book" } end trait :named_second do name { "my second book" } end end end # 用例中直接使用特质 let!(:book){FactoryGirl.create(:book, :named_new)} let!(:book2){FactoryGirl.create(:book, :named_second)}
- 如果用例不需要验证数据库写入逻辑,也可以直接修改内存对象属性,不需要调用数据库更新方法:
before(:each) do book.name = "my new book" book2.name = "my second book" end
内容的提问来源于stack exchange,提问作者jegan
相关产品推荐
相关产品推荐

