在FactoryBot上指定所需的字段

时间:2018-04-12 14:35:42

标签: ruby-on-rails ruby rspec factory-bot

我有一个从rest API获取信息的对象。让我们说我从API调用帖子,我可以指定我想要提取的字段。只是它的外观的一个例子:

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

这是一个API调用,它返回一个带有这些指定字段的Post对象。

为了进行测试,我正在使用Factory Bot对此API调用进行存根,并直接返回一个Post对象,其中包含可以查询的所有可能字段。

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

所以我想尝试实现(在FactoryBot中):

build(:post, fields: [:id,:title])并设置一个只有id和title的Post对象。

如果我执行build(:post, fields: [:title, :created_at])并使用title和created_at设置Post对象。等等...

做了一些研究并尝试了一些想法,但在所有这些想法中都未能解决如何建立这种流程。

有关如何实现此行为的任何想法?

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

3 个答案:

答案 0 :(得分:2)

让我们假设这是你的邮政工厂:

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

当你致电build(:post)时,它会创建一个带有title,created_at和描述集的对象。

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

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')将创建一个帖子对象,其中只设置标题和描述(created_at将为零)
  2. 调用build(:post, :all_fields)将创建一个post对象,将设置所有字段。
  3. 修改

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

    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)创建包含所有字段的帖子
    2. build(:post, fields: ['description', 'title'])创建一个帖子,其中除了描述和标题之外的所有内容都是nil
    3. 此解决方案应该按预期工作,但它可能会减慢您的测试速度(我觉得它看起来不太好:) :)

答案 1 :(得分:0)

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

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

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

现在,当您构建帖子时,它将如下所示:

>> 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>

答案 2 :(得分:0)

FactoryBot允许您通过传递属性哈希来覆盖工厂 - 所以为什么不将属性设置为nil:

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