如何用FactoryBot引用关联记录?Rails模型关联测试实操问题
问题描述
我有一个Ruby on Rails应用,包含以下模型:
class User < ApplicationRecord has_many :crews, dependent: :destroy end
class Crew < ApplicationRecord belongs_to :user end
每次创建crew模型时必须关联user模型,否则保存时会抛出错误。
我在单元测试中使用FactoryBot构建所有测试模型,希望能同时创建user模型及其依赖的crew模型,且之后可以引用该crew模型。
现有如下user工厂:
FactoryBot.define do factory :user do email { 'urist@mcfortress.com' } factory :user_with_crew do after(:create) do |user| create(:crew, user: user) end end end end
我可以在测试中这样调用:
let(:user) { create :user_with_crew }
但之后无法引用测试所需的crew模型。我可以在before块中手动创建和赋值,但希望借助FactoryBot保持代码DRY,请问该如何实现?
解决方案
方法1:利用模型关联直接获取
因为User和Crew是一对多关联,创建带crew的user后,直接通过user.crews.first就能拿到对应的crew:
测试代码示例:
let(:user) { create :user_with_crew } let(:crew) { user.crews.first }
如果想确保只创建一个crew,可以修改工厂避免重复:
FactoryBot.define do factory :user do email { 'urist@mcfortress.com' } factory :user_with_crew do after(:create) do |user| user.crews.clear create(:crew, user: user) end end end end
方法2:用Trait实现灵活配置
Trait是FactoryBot复用逻辑的常用方式,比嵌套工厂更灵活:
FactoryBot.define do factory :user do email { 'urist@mcfortress.com' } trait :with_crew do after(:create) do |user| create(:crew, user: user) end end end end
测试中调用方式:
let(:user) { create(:user, :with_crew) } let(:crew) { user.crews.first }
方法3:给User添加临时属性(可选)
如果想直接通过user.crew访问单个关联的crew,可以在工厂里给user赋值临时属性:
FactoryBot.define do factory :user do email { 'urist@mcfortress.com' } factory :user_with_crew do after(:create) do |user| user.instance_variable_set(:@crew, create(:crew, user: user)) def user.crew @crew || crews.first end end end end end
测试中直接调用:
let(:user) { create :user_with_crew } let(:crew) { user.crew }
方法4:反向创建(以Crew为核心)
如果测试场景更关注Crew,可以修改Crew工厂,自动关联User:
FactoryBot.define do factory :crew do user { create(:user) } end end
测试中直接创建Crew即可同时拿到关联的User:
let(:crew) { create(:crew) } let(:user) { crew.user }
内容的提问来源于stack exchange,提问作者StaticMethod
相关产品推荐
相关产品推荐

