在 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
然后:
- 调用
build(:post, title: 'Post title', description: 'Post description')
将创建一个 post object,其中仅设置了标题和描述(created_at 将为零)
- 调用
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
那么,你可以这样称呼它:
build(:post)
创建 post 所有字段
build(:post, fields: ['description', 'title'])
创建一个 post,其中除了描述和标题之外的所有内容都是 nil
.
此解决方案应按预期工作,但它可能会减慢您的测试速度(而且我认为它看起来不太好:))
FactoryBot 中的最佳做法是让您的标准工厂生产仅包含模型所需字段的对象。 (请注意,FactoryBot 会自动为您生成 id
。)
假设您有一个名为 Post 的模型,它只需要 title
,但有可选字段 description
和 date
。你的工厂看起来像这样:
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
})
我有一个 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
然后:
- 调用
build(:post, title: 'Post title', description: 'Post description')
将创建一个 post object,其中仅设置了标题和描述(created_at 将为零) - 调用
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
那么,你可以这样称呼它:
build(:post)
创建 post 所有字段build(:post, fields: ['description', 'title'])
创建一个 post,其中除了描述和标题之外的所有内容都是nil
.
此解决方案应按预期工作,但它可能会减慢您的测试速度(而且我认为它看起来不太好:))
FactoryBot 中的最佳做法是让您的标准工厂生产仅包含模型所需字段的对象。 (请注意,FactoryBot 会自动为您生成 id
。)
假设您有一个名为 Post 的模型,它只需要 title
,但有可选字段 description
和 date
。你的工厂看起来像这样:
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
})