能否定义仅创建单个实例并始终返回该实例的FactoryGirl工厂?
如何让FactoryGirl返回同一个实例?
当然可以实现!根据你需要的复用范围,有两种实用方案,我给你拆解清楚:
情况1:单个测试用例里复用同一实例
其实你现在写的代码本身就已经满足这个需求了!RSpec的let是惰性加载的——只有当你首次调用thing的时候,才会执行FactoryGirl.create(:thing)创建实例,之后在同一个it/spec块里不管调用多少次thing,都会返回第一次创建的那个对象。
举个实际测试的例子,你可以验证一下:
describe Thing do let(:thing) { FactoryGirl.create(:thing) } it "reuses the same instance every time" do first_instance = thing second_instance = thing # 两个变量的内存地址一致,说明是同一个实例 expect(first_instance.object_id).to eq(second_instance.object_id) end end
情况2:跨多个测试用例复用全局实例
如果是想让整个测试套件里的所有spec都共享同一个Thing实例(比如某些需要全局固定数据的场景),可以结合before(:suite)和let来实现:
RSpec.describe Thing do # 在整个测试套件启动前,只创建一次实例 before(:suite) do $global_thing = FactoryGirl.create(:thing) end # 所有测试用例里的thing都指向这个全局实例 let(:thing) { $global_thing } it "uses the global instance in first test" do expect(thing).to eq($global_thing) end it "still uses the same instance in second test" do expect(thing.object_id).to eq($global_thing.object_id) end end
⚠️ 注意:这种全局共享的方式可能会让测试之间产生耦合——比如某个测试修改了实例的属性,会影响其他测试的结果。除非你明确知道自己需要这种全局状态,否则尽量不要用这种方式哦。
另外,你也可以给工厂加个trait来实现单例逻辑(不过作用范围还是单个测试用例):
# 在你的工厂定义文件里 FactoryGirl.define do factory :thing do # 这里写你的工厂属性,比如name { "Test Thing" } trait :singleton do after(:create) do |instance| @singleton_thing ||= instance end end end end
然后在测试里调用:
let(:thing) { FactoryGirl.create(:thing, :singleton) }
这种方式下,同一个测试用例里多次调用create(:thing, :singleton)也只会返回第一次创建的实例。
内容的提问来源于stack exchange,提问作者blogofsongs
相关产品推荐
相关产品推荐

