Testing Enum Trait Generation and Handling in Factory Bot
This test suite validates the enum traits functionality in Factory Bot, focusing on automatic trait generation for enum fields in ActiveRecord models. It covers both automatic and manual trait definition scenarios, ensuring proper integration with different Ruby on Rails versions.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
thoughtbot/factory_bot
spec/acceptance/enum_traits_spec.rb
describe "enum traits" do
def define_model_with_enum(class_name, field, values)
define_model(class_name, status: :integer) do
if ActiveRecord::VERSION::STRING >= "7.0"
enum field, values
else
enum field => values
end
end
end
context "when automatically_define_enum_traits is true" do
it "builds traits automatically for model enum field" do
define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2})
FactoryBot.define do
factory :task
end
Task.statuses.each_key do |trait_name|
task = FactoryBot.build(:task, trait_name)
expect(task.status).to eq(trait_name)
end
Task.reset_column_information
end
it "prefers user defined traits over automatically built traits" do
define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2})
FactoryBot.define do
factory :task do
trait :queued do
status { :finished }
end
trait :started do
status { :finished }
end
trait :finished do
status { :finished }
end
end
end
Task.statuses.each_key do |trait_name|
task = FactoryBot.build(:task, trait_name)
expect(task.status).to eq("finished")
end
Task.reset_column_information
end
it "builds traits for each enumerated value using a provided list of values as a Hash" do
statuses = {queued: 0, started: 1, finished: 2}
define_class "Task" do
attr_accessor :status
end
FactoryBot.define do
factory :task do
traits_for_enum :status, statuses
end
end
statuses.each do |trait_name, trait_value|
task = FactoryBot.build(:task, trait_name)
expect(task.status).to eq(trait_value)
end
end
it "builds traits for each enumerated value using a provided list of values as an Array" do
statuses = %w[queued started finished]
define_class "Task" do
attr_accessor :status
end
FactoryBot.define do
factory :task do
traits_for_enum :status, statuses
end
end
statuses.each do |trait_name|
task = FactoryBot.build(:task, trait_name)
expect(task.status).to eq(trait_name)
end
end
it "builds traits for each enumerated value using a custom enumerable" do
statuses = define_class("Statuses") {
include Enumerable
def each(&block)
["queued", "started", "finished"].each(&block)
end
}.new
define_class "Task" do
attr_accessor :status
end
FactoryBot.define do
factory :task do
traits_for_enum :status, statuses
end
end
statuses.each do |trait_name|
task = FactoryBot.build(:task, trait_name)
expect(task.status).to eq(trait_name)
end
end
end
context "when automatically_define_enum_traits is false" do
it "raises an error for undefined traits" do
with_temporary_assignment(FactoryBot, :automatically_define_enum_traits, false) do
define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2})
FactoryBot.define do
factory :task
end
Task.statuses.each_key do |trait_name|
expect { FactoryBot.build(:task, trait_name) }.to raise_error(
KeyError, "Trait not registered: \"#{trait_name}\""
)
end
Task.reset_column_information
end
end
it "builds traits for each enumerated value when traits_for_enum are specified" do
with_temporary_assignment(FactoryBot, :automatically_define_enum_traits, false) do
define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2})
FactoryBot.define do
factory :task do
traits_for_enum(:status)
end
end
Task.statuses.each_key do |trait_name|
task = FactoryBot.build(:task, trait_name)
expect(task.status).to eq(trait_name)
end
Task.reset_column_information
end
end
end
end