diff --git a/docs/src/traits/enum.md b/docs/src/traits/enum.md index 068a3b6c1..156dcbf15 100644 --- a/docs/src/traits/enum.md +++ b/docs/src/traits/enum.md @@ -59,6 +59,25 @@ FactoryBot.define do end ``` +If you want to override `FactoryBot.automatically_define_enum_traits` on a +per-model basis, you can use an additional attribute on your factory: + +```rb +FactoryBot.define do + factory :task, automatically_define_enum_traits: false do + status { :queued } + + trait :in_progress do + status { :started } + end + + trait :complete do + status {:finished } + end + end +end +``` + It is also possible to use this feature for other enumerable values, not specifically tied to Active Record enum attributes. diff --git a/lib/factory_bot/definition.rb b/lib/factory_bot/definition.rb index b393882a3..49d9f643d 100644 --- a/lib/factory_bot/definition.rb +++ b/lib/factory_bot/definition.rb @@ -17,6 +17,7 @@ def initialize(name, base_traits = [], **opts) @constructor = nil @attributes = nil @compiled = false + @automatically_define_enum_traits = opts[:automatically_define_enum_traits] @expanded_enum_traits = false end @@ -203,8 +204,13 @@ def automatically_register_defined_enums(klass) end def automatically_register_defined_enums?(klass) - FactoryBot.automatically_define_enum_traits && - klass.respond_to?(:defined_enums) + automatically_define_enum_traits = if @automatically_define_enum_traits.nil? + FactoryBot.automatically_define_enum_traits + else + @automatically_define_enum_traits + end + + automatically_define_enum_traits && klass.respond_to?(:defined_enums) end end end diff --git a/lib/factory_bot/factory.rb b/lib/factory_bot/factory.rb index 3e34107c0..b92ea8c77 100644 --- a/lib/factory_bot/factory.rb +++ b/lib/factory_bot/factory.rb @@ -13,7 +13,12 @@ def initialize(name, options = {}) @aliases = options[:aliases] || [] @class_name = options[:class] @uri_manager = FactoryBot::UriManager.new(names) - @definition = Definition.new(@name, options[:traits] || [], uri_manager: @uri_manager) + @definition = Definition.new( + @name, + options[:traits] || [], + automatically_define_enum_traits: options[:automatically_define_enum_traits], + uri_manager: @uri_manager + ) @compiled = false end @@ -148,7 +153,7 @@ def compiled_constructor private def assert_valid_options(options) - options.assert_valid_keys(:class, :parent, :aliases, :traits) + options.assert_valid_keys(:class, :parent, :aliases, :traits, :automatically_define_enum_traits) end def parent diff --git a/spec/acceptance/enum_traits_spec.rb b/spec/acceptance/enum_traits_spec.rb index 5c6a20ecf..f0e54573b 100644 --- a/spec/acceptance/enum_traits_spec.rb +++ b/spec/acceptance/enum_traits_spec.rb @@ -9,7 +9,7 @@ def define_model_with_enum(class_name, field, values) end end - context "when automatically_define_enum_traits is true" do + context "when FactoryBot.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}) @@ -119,9 +119,45 @@ def each(&block) expect(task.status).to eq(trait_name) end end + + context "when the factory specifies automatically_define_enum_traits as false" do + it "raises an error for undefined traits" do + define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2}) + + FactoryBot.define do + factory :task, automatically_define_enum_traits: false + 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 + + it "builds traits for each enumerated value when traits_for_enum are specified" do + define_model_with_enum("Task", :status, {queued: 0, started: 1, finished: 2}) + + FactoryBot.define do + factory :task, automatically_define_enum_traits: false 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 - context "when automatically_define_enum_traits is false" do + context "when FactoryBot.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}) @@ -159,5 +195,25 @@ def each(&block) Task.reset_column_information end end + + context "when the factory specifies automatically_define_enum_traits as true" do + it "builds traits automatically for model enum field" 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, automatically_define_enum_traits: true + 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 end