如何在 FactoryBot 中设置不是外键的 _id 值?
How do I set an _id value in FactoryBot that isn't a foreign key?
我正在使用 FactoryBot(以前称为 FactoryGirl)为我的测试创建一些工厂数据。我有一个通过架构看起来像这样的模型(精简为相关内容):
create_table "items", force: :cascade do |t|
t.text "team"
t.text "feature_id"
t.text "feature"
t.timestamps
end
但是,feature_id
和 feature
不是对要素对象的引用...它们只是字符串。
我这样定义我的工厂:
FactoryBot.define do
factory :item do
team "TheTeam"
sequence(:feature_id) {|n| "Feature#{n}" }
feature { Faker::Lorem.sentence }
end
end
简单的案例有效:
> FactoryBot.create(:item)
=> #<Item:0x007fad8cbfc048
id: 1,
team: "TheTeam",
feature_id: "Feature1",
feature: "Qui voluptatem animi et rerum et.",
created_at: Wed, 10 Jan 2018 02:40:01 UTC +00:00,
updated_at: Wed, 10 Jan 2018 02:40:01 UTC +00:00>
但是当我想指定我自己的 feature_id
时,会发生这种情况:
> FactoryBot.create(:item, feature_id: "123")
=> #<Item:0x007fad8d30a880
id: 2,
team: "TheTeam",
feature_id: "123",
feature: nil,
created_at: Wed, 10 Jan 2018 02:40:59 UTC +00:00,
updated_at: Wed, 10 Jan 2018 02:40:59 UTC +00:00>
您可以看到 feature
现在是 nil
。我假设这是因为它试图推断 feature_id
和 feature
在某种程度上是相关的。但在这种情况下,我不希望他们成为。
是否有更好的方法来定义工厂,以便将它们视为不相关的字段?
顺便说一句,如果我尝试同时设置 feature_id
和 feature
,它看起来像这样:
> FactoryBot.create(:item, feature_id: "123", feature: "hi")
=> #<Item:0x007fad8d262810
id: 3,
team: "TheTeam",
feature_id: nil,
feature: nil,
created_at: Wed, 10 Jan 2018 02:45:01 UTC +00:00,
updated_at: Wed, 10 Jan 2018 02:45:01 UTC +00:00>
所以它只是将两个字段设置为 nil
。我怀疑 FactoryBot 正试图根据这些字段的名称 "smart"。我会更改它们,但它们已经在 Db 中以这种方式设置。
看来 FactoryBot 确实在做出假设,而我还没有找到改变这些假设的方法。可能值得打开一个问题,看看维护者必须提供什么。
同时,这里有一个解决方法:
FactoryBot.define do
FEATURE_IDS ||= (1..1000).cycle
factory :item do
team "TheTeam"
transient { without_feature_id false }
transient { without_feature false }
after(:build, :stub) do |item, evaluator|
item.feature_id = "Feature#{FEATURE_IDS.next}" unless evaluator.without_feature_id
item.feature = Faker::Lorem.sentence unless evaluator.without_feature
end
end
end
这将在您上述情况下正常运行。
增量是棘手的。我找不到在资源构建上下文之外使用 FactoryBot 序列的方法,因此我使用 Enumerator 并调用 #next
来创建序列。这与 FactoryBot 序列的工作方式类似,只是在测试过程中无法重置为 1 运行.
RSpec 测试证明它按预期工作,无论我们是在数据库中创建项目还是在内存中构建项目:
context 'when more than one item is created' do
let(:item_1) { create(:item) }
let(:item_2) { create(:item) }
it 'increments feature_id by 1' do
expect(item_1.feature_id).to be_present
expect(item_2.feature_id).to eq(item_1.feature_id.next)
end
end
context 'when using build instead of create' do
let(:item_1) { build(:item) }
let(:item_2) { build(:item) }
it 'increments feature_id by 1' do
expect(item_1.feature_id).to be_present
expect(item_2.feature_id).to eq(item_1.feature_id.next)
end
end
请注意,如果没有 feature_id 或使用典型构造的功能,您将无法创建项目;例如:
>> item = create(:item, feature_id: nil)
将导致
>> item.feature_id
#> "Feature1"
如果您希望创建一个没有特征和 feature_id 字段的对象,您可以这样做:
create(:item, without_feature_id: true, without_feature: true)
我正在使用 FactoryBot(以前称为 FactoryGirl)为我的测试创建一些工厂数据。我有一个通过架构看起来像这样的模型(精简为相关内容):
create_table "items", force: :cascade do |t|
t.text "team"
t.text "feature_id"
t.text "feature"
t.timestamps
end
但是,feature_id
和 feature
不是对要素对象的引用...它们只是字符串。
我这样定义我的工厂:
FactoryBot.define do
factory :item do
team "TheTeam"
sequence(:feature_id) {|n| "Feature#{n}" }
feature { Faker::Lorem.sentence }
end
end
简单的案例有效:
> FactoryBot.create(:item)
=> #<Item:0x007fad8cbfc048
id: 1,
team: "TheTeam",
feature_id: "Feature1",
feature: "Qui voluptatem animi et rerum et.",
created_at: Wed, 10 Jan 2018 02:40:01 UTC +00:00,
updated_at: Wed, 10 Jan 2018 02:40:01 UTC +00:00>
但是当我想指定我自己的 feature_id
时,会发生这种情况:
> FactoryBot.create(:item, feature_id: "123")
=> #<Item:0x007fad8d30a880
id: 2,
team: "TheTeam",
feature_id: "123",
feature: nil,
created_at: Wed, 10 Jan 2018 02:40:59 UTC +00:00,
updated_at: Wed, 10 Jan 2018 02:40:59 UTC +00:00>
您可以看到 feature
现在是 nil
。我假设这是因为它试图推断 feature_id
和 feature
在某种程度上是相关的。但在这种情况下,我不希望他们成为。
是否有更好的方法来定义工厂,以便将它们视为不相关的字段?
顺便说一句,如果我尝试同时设置 feature_id
和 feature
,它看起来像这样:
> FactoryBot.create(:item, feature_id: "123", feature: "hi")
=> #<Item:0x007fad8d262810
id: 3,
team: "TheTeam",
feature_id: nil,
feature: nil,
created_at: Wed, 10 Jan 2018 02:45:01 UTC +00:00,
updated_at: Wed, 10 Jan 2018 02:45:01 UTC +00:00>
所以它只是将两个字段设置为 nil
。我怀疑 FactoryBot 正试图根据这些字段的名称 "smart"。我会更改它们,但它们已经在 Db 中以这种方式设置。
看来 FactoryBot 确实在做出假设,而我还没有找到改变这些假设的方法。可能值得打开一个问题,看看维护者必须提供什么。
同时,这里有一个解决方法:
FactoryBot.define do
FEATURE_IDS ||= (1..1000).cycle
factory :item do
team "TheTeam"
transient { without_feature_id false }
transient { without_feature false }
after(:build, :stub) do |item, evaluator|
item.feature_id = "Feature#{FEATURE_IDS.next}" unless evaluator.without_feature_id
item.feature = Faker::Lorem.sentence unless evaluator.without_feature
end
end
end
这将在您上述情况下正常运行。
增量是棘手的。我找不到在资源构建上下文之外使用 FactoryBot 序列的方法,因此我使用 Enumerator 并调用 #next
来创建序列。这与 FactoryBot 序列的工作方式类似,只是在测试过程中无法重置为 1 运行.
RSpec 测试证明它按预期工作,无论我们是在数据库中创建项目还是在内存中构建项目:
context 'when more than one item is created' do
let(:item_1) { create(:item) }
let(:item_2) { create(:item) }
it 'increments feature_id by 1' do
expect(item_1.feature_id).to be_present
expect(item_2.feature_id).to eq(item_1.feature_id.next)
end
end
context 'when using build instead of create' do
let(:item_1) { build(:item) }
let(:item_2) { build(:item) }
it 'increments feature_id by 1' do
expect(item_1.feature_id).to be_present
expect(item_2.feature_id).to eq(item_1.feature_id.next)
end
end
请注意,如果没有 feature_id 或使用典型构造的功能,您将无法创建项目;例如:
>> item = create(:item, feature_id: nil)
将导致
>> item.feature_id
#> "Feature1"
如果您希望创建一个没有特征和 feature_id 字段的对象,您可以这样做:
create(:item, without_feature_id: true, without_feature: true)