我正在使用FactoryBot(以前为FactoryGirl)为测试创建一些工厂数据。我有一个通过模式看起来像这样的模型(简化为相关内容):

create_table "items", force: :cascade do |t|
    t.text "team"
    t.text "feature_id"
    t.text "feature"
    t.timestamps
end

但是,feature_idfeature不是对要素对象的引用,它们只是字符串。

我已经这样定义了我的工厂:
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_idfeature是某种相关的。但是在这种情况下,我不希望他们成为。

是否有更好的方法来定义工厂,以便仅将它们视为不相关的字段?

顺便说一句,如果我尝试同时设置feature_idfeature,它看起来像这样:
> 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试图根据它们的名称对这些字段“智能”。我会更改它们,但是它们已经在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和feature_id字段的对象,则可以执行以下操作:
create(:item, without_feature_id: true, without_feature: true)

关于ruby-on-rails - 如何在FactoryBot中设置不是外键的_id值?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48179555/

10-13 01:13