如何创建合并两条其他记录的FactoryBot测试工厂?
FactoryBot 适配非ActiveRecord组合实体的实现方案
FactoryBot 原生支持自定义对象初始化、持久化逻辑,不需要修改现有TransitProvider的业务代码,就能实现你要的调用效果,具体实现如下:
- 首先确认两个底层ActiveRecord模型
Provider和TransitService已经配置好可用的FactoryBot工厂,调用create(:provider)、create(:transit_service)可以正常生成持久化测试记录,这是后续组装的基础。 - 编写
transit_provider工厂,核心是重写默认的to_create回调,替换掉FactoryBot默认调用ActiveRecordsave方法的逻辑,改为「先创建底层依赖记录→组装TransitProvider实例」的流程,同时可以自定义initialize_with逻辑适配build方法(不写库、仅生成内存实例)的场景。
完整工厂代码示例:
# spec/factories/transit_providers.rb FactoryBot.define do factory :transit_provider do # 定义对外暴露的属性,和你要传的参数对应 name { "Default Transit Provider" } # 自定义build逻辑:不写数据库,仅生成内存对象 initialize_with do temp_provider = build(:provider, name: name) temp_service = build(:transit_service, provider: temp_provider) new(provider: temp_provider, transit_service: temp_service) end # 自定义create逻辑:持久化底层依赖记录,再组装实例 to_create do |instance| # 1. 持久化底层Provider记录,传入对应属性 persisted_provider = create(:provider, name: instance.name) # 2. 持久化关联的TransitService记录,绑定刚创建的Provider persisted_service = create(:transit_service, provider: persisted_provider) # 3. 给TransitProvider实例赋值底层关联对象,完成组装 instance.provider = persisted_provider instance.transit_service = persisted_service # 如果你的TransitProvider是通过自定义类方法加载的(比如TransitProvider.find(persisted_provider.id)) # 可以直接把实例替换为类方法返回的对象,保证和生产环境的初始化逻辑完全一致 instance end end end
适配提示:
如果TransitProvider的属性需要分别映射到两个底层模型的不同字段(比如contact_phone对应Provider表的phone字段、service_area对应TransitService表的area字段),可以在工厂里用transient定义临时属性做中转,不需要修改TransitProvider本身的业务代码。示例:transient do contact_phone { "13800000000" } service_area { "Downtown" } end to_create do |instance| persisted_provider = create(:provider, name: instance.name, phone: instance.contact_phone) persisted_service = create(:transit_service, provider: persisted_provider, area: instance.service_area) instance.provider = persisted_provider instance.transit_service = persisted_service instance end
配置完成后,你预期的调用代码就可以正常运行:
let(:transit_provider) { create(:transit_provider, name: "Some Name") }
执行时会自动在测试库生成对应的Provider、TransitService持久化记录,最终返回组装完成的TransitProvider实例,属性会正确读取传入的参数。
内容的提问来源于stack exchange,提问作者Dan Sharp
相关产品推荐
相关产品推荐

