在 FactoryBot 上指定您想要的字段

Specify fields you want on FactoryBot

我有一个 object 从休息 API 获取信息。假设我从 API 调用 Posts,我可以指定要提取的字段。只是一个外观示例:

Post.find!(id: 1, fields: [:id, :title, :description])

这执行了一个 API 调用,它 returns 我使用这些指定的字段 Post object。

为了测试,我将此 API 调用与 Factory Bot 和 returns 直接存根为 Post object,它可以查询所有可能的字段。

这种方法不是最好的,因为测试总是返回所有字段和代码本身,也许我只需要几个字段而不是全部

所以我正在尝试实现类似(在 FactoryBot 中)的目标:

build(:post, fields: [:id,:title]) 并设置一个 Post object 仅包含 ID 和标题。

如果我 build(:post, fields: [:title, :created_at]) 并设置 Post object 仅包含标题和 created_at。等等...

进行了一些研究并尝试了一些想法,但关于如何构建此流程的所有这些都失败了。

知道如何实现这种行为吗?

编辑 Traits 似乎是一个不错的选择,但我必须与 API 调用一样一致,指定这些 fields。所以特质对我不起作用...

我们假设这是您的 Post:

工厂
FactoryBot.define do
  factory :post do
    sequence(:title) { |n| "Post no. #{n}" }
    description 'Post description'
    created_at { DateTime.now }
  end
end

当您调用 build(:post) 时,它将创建一个带有标题、created_at 和描述集的 object。

但是,如果您要从工厂中删除这些字段(或将它们移到 trait 下):

FactoryBot.define do
  factory :post do

    trait :all_fields do
      sequence(:title) { |n| "Post no. #{n}" }
      description 'Post description'
      created_at { DateTime.now }
    end
  end
end

然后:

  1. 调用 build(:post, title: 'Post title', description: 'Post description') 将创建一个 post object,其中仅设置了标题和描述(created_at 将为零)
  2. 调用 build(:post, :all_fields) 将创建一个 post object 将设置所有字段。

编辑

我想我现在更了解这个问题了。让我们假设这是你的工厂:

FactoryBot.define do
  factory :post do
    sequence(:title) { |n| "Post no. #{n}" }
    created_at { DateTime.now }
    description 'Post description'
  end
end

改为:

FactoryBot.define do
  factory :post do
    transient do
      fields []
    end

    sequence(:title) { |n| "Post no. #{n}" }
    created_at { DateTime.now }
    description 'Post description'

    after :build do |post, evaluator|
      unless evaluator.fields.empty? do
        (post.attributes.keys - evaluator.fields).each do |attribute_to_remove|
          post.send("#{attribute_to_remove}=", nil)
        end
      end
    end
  end
end

那么,你可以这样称呼它:

  1. build(:post) 创建 post 所有字段
  2. build(:post, fields: ['description', 'title']) 创建一个 post,其中除了描述和标题之外的所有内容都是 nil.

此解决方案应按预期工作,但它可能会减慢您的测试速度(而且我认为它看起来不太好:))

FactoryBot 中的最佳做法是让您的标准工厂生产仅包含模型所需字段的对象。 (请注意,FactoryBot 会自动为您生成 id。)

假设您有一个名为 Post 的模型,它只需要 title,但有可选字段 descriptiondate。你的工厂看起来像这样:

FactoryBot.define do
  factory :post do
    title 'Post Title'
  end
end

现在当你构建一个 Post 时,它看起来像这样:

>> FactoryBot.build_stubbed(:post)
#> <Post id: 1001, title: "Post Title", description: nil, date: nil>

您可以根据具体情况添加可选字段:

>> FactoryBot.build_stubbed(:post, description: 'This is a test post.')
#> <Post id: 1001, title: "Post Title", description: 'This is a test post.', date: nil>

或者您可以在工厂内添加特征:

FactoryBot.define do
  factory :post do
    title 'Post Title'

    trait :with_description_and_date do
      description 'This is a test post.'
      date Date.today
    end
  end
end

>> FactoryBot.build_stubbed(:post, :with_description_and_date)
#> <Post id: 1001, title: "Post Title", description: 'This is a test post.', date: Thu, 12 Apr 2018>

FactoryBot 允许您通过传递属性散列来覆盖工厂 - 那么为什么不直接将属性设置为 nil:

build(:post, {
  title: nil,
  description: nil
})