diff --git a/docs/src/callbacks/callback_order.md b/docs/src/callbacks/callback_order.md index 2090e00af..fbbd8feb6 100644 --- a/docs/src/callbacks/callback_order.md +++ b/docs/src/callbacks/callback_order.md @@ -17,29 +17,35 @@ FactoryBot.define do factory :user do before(:all) { puts "User before(:all)" } after(:all) { puts "User after(:all)" } - after(:build) { puts "User after(:build)" - + before(:build) { puts "User before(:build)" } + after(:build) { puts "User after(:build)" } + trait :trait_a do - after(:build) { puts "Trait-A after(:build)" + before(:build) { puts "Trait-A before(:build)" } + after(:build) { puts "Trait-A after(:build)" } end - - trait :trait_b do + + trait :trait_b do + before(:build) { puts "Trait-B before(:build)" } after(:build) { puts "Trait-B after(:build)" } end end end -build(:user, :trait_b, :trait_a) +build(:user, :trait_b, :trait_a) # Result: # # 1. "Global before(:all)" # 2. "User before(:all)" -# 3. "User after(:build)" -# 4. "Trait-B after(:build)" -# 5. "Trait-A after(:build)" -# 6. "Global after(:all)" -# 7. "User after(:all)" +# 3. "User before(:build) +# 4. "Trait-B before(:build)" +# 5. "Trait-A before(:build)" +# 6. "User after(:build)" +# 7. "Trait-B after(:build)" +# 8. "Trait-A after(:build)" +# 9. "Global after(:all)" +# 10. "User after(:all)" ``` @@ -49,29 +55,35 @@ build(:user, :trait_b, :trait_a) ```ruby FactoryBot.define do before(:all) { puts "Global before(:all)" } + before(:build) { puts "Global before(:build)" } after(:build) { puts "Global after(:build)" } after(:all) { puts "Global after(:all)" } factory :parent do before(:all) { puts "Parent before(:all)" } + before(:build) { puts "Parent before(:build)" } after(:all) { puts "Parent after(:all)" } after(:build) { puts "Parent after(:build)" } - - trait :trait_a do + + trait :trait_a do + before(:build) { puts "Trait-A before(:build)" } after(:build) { puts "Trait-A after(:build)" } end factory :child do before(:all) { puts "Child before(:all)" } + before(:build) { puts "Child before(:build)" } after(:build) { puts "Child after(:build)" } after(:all) { puts "Child after(:all)" } trait :trait_b do + before(:build) { puts "Trait-B before(:build)" } after(:build) { puts "Trait-B after(:build)" } after(:all) { puts "Trait-B after(:all)" } end trait :trait_c do + before(:build) { puts "Trait-C before(:build)" } after(:build) { puts "Trait-C after(:build)" } before(:all) { puts "Trait-C before(:all)" } end @@ -79,7 +91,7 @@ FactoryBot.define do end end -build(:child, :trait_c, :trait_a, :trait_b) +build(:child, :trait_c, :trait_a, :trait_b) # Result: # @@ -87,15 +99,20 @@ build(:child, :trait_c, :trait_a, :trait_b) # 2. "Parent before(:all)" # 3. "Child before(:all)" # 4. "Trait-C before(:all)" -# 5. "Global after(:build)" -# 6. "Parent after(:build)" -# 7. "Child after(:build)" -# 8. "Trait-C after(:build)" -# 9. "Trait-A after(:build)" -# 10. "Trait-B after(:build)" -# 11. "Global after(:all)" -# 12. "Parent after(:all)" -# 13. "Child after(:all)" -# 14. "Trait-B after(:all)" - +# 5. "Global before(:build)" +# 6. "Parent before(:build)" +# 7. "Child before(:build)" +# 8. "Trait-C before(:build)" +# 9. "Trait-A before(:build)" +# 10. "Trait-B before(:build)" +# 11. "Global after(:build)" +# 12. "Parent after(:build)" +# 13. "Child after(:build)" +# 14. "Trait-C after(:build)" +# 15. "Trait-A after(:build)" +# 16. "Trait-B after(:build)" +# 17. "Global after(:all)" +# 18. "Parent after(:all)" +# 19. "Child after(:all)" +# 20. "Trait-B after(:all)" ``` diff --git a/docs/src/callbacks/default-callbacks.md b/docs/src/callbacks/default-callbacks.md index f8e3a24d3..e5b14910c 100644 --- a/docs/src/callbacks/default-callbacks.md +++ b/docs/src/callbacks/default-callbacks.md @@ -2,10 +2,13 @@ factory\_bot makes available four callbacks for injecting some code: -* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`) -* before(:create) - called before a factory is saved (via `FactoryBot.create`) -* after(:create) - called after a factory is saved (via `FactoryBot.create`) -* after(:stub) - called after a factory is stubbed (via `FactoryBot.build_stubbed`) +* before(:all) - called before any strategy is used (e.g., `FactoryBot.build`, `FactoryBot.create`, `FactoryBot.build_stubbed`) +* before(:build) - called before a factory is built (via `FactoryBot.build`, `FactoryBot.create`) +* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`) +* before(:create) - called before a factory is saved (via `FactoryBot.create`) +* after(:create) - called after a factory is saved (via `FactoryBot.create`) +* after(:stub) - called after a factory is stubbed (via `FactoryBot.build_stubbed`) +* after(:all) - called after any strategy is used (e.g., `FactoryBot.build`, `FactoryBot.create`, `FactoryBot.build_stubbed`) Examples: diff --git a/docs/src/callbacks/summary.md b/docs/src/callbacks/summary.md index e49b26ba6..9f4f45fa7 100644 --- a/docs/src/callbacks/summary.md +++ b/docs/src/callbacks/summary.md @@ -4,12 +4,13 @@ factory\_bot makes six callbacks available: | Callback | Timing | | --------------- | ------------------------------------------------------------------------------------------------------------------------- | -| before(:all) | called before a factory constructs an object (via `FactoryBot.build`, `FactoryBot.create`, or `FactoryBot.build_stubbed`) | +| before(:all) | called before any strategy is used to construct an object, including custom strategies | +| before(:build) | called before a factory builds an object (via `FactoryBot.build` or `FactoryBot.create`) | | after(:build) | called after a factory builds an object (via `FactoryBot.build` or `FactoryBot.create`) | | before(:create) | called before a factory saves an object (via `FactoryBot.create`) | | after(:create) | called after a factory saves an object (via `FactoryBot.create`) | | after(:stub) | called after a factory stubs an object (via `FactoryBot.build_stubbed`) | -| after(:all) | called after a factory constructs an object (via `FactoryBot.build`, `FactoryBot.create`, or `FactoryBot.build_stubbed`) | +| after(:all) | called after any strategy has completed, including custom strategies | ## Examples @@ -17,7 +18,7 @@ factory\_bot makes six callbacks available: ### Calling an object's own method after building ```ruby -## +## # Define a factory that calls the generate_hashed_password method # after the user factory is built. # @@ -32,11 +33,11 @@ end ```ruby ## -# Disable a model's own :after_create callback that sends an email +# Disable a model's own :after_create callback that sends an email # on creation, then re-enable it afterwards # factory :user do before(:all){ User.skip_callback(:create, :after, :send_welcome_email) } after(:all){ User.set_callback(:create, :after, :send_welcome_email) } end -``` \ No newline at end of file +``` diff --git a/lib/factory_bot/strategy/build.rb b/lib/factory_bot/strategy/build.rb index d18bb8ff4..5716359d3 100644 --- a/lib/factory_bot/strategy/build.rb +++ b/lib/factory_bot/strategy/build.rb @@ -6,6 +6,8 @@ def association(runner) end def result(evaluation) + evaluation.notify(:before_build, nil) + evaluation.object.tap do |instance| evaluation.notify(:after_build, instance) end diff --git a/lib/factory_bot/strategy/create.rb b/lib/factory_bot/strategy/create.rb index c9371f42d..d292d6085 100644 --- a/lib/factory_bot/strategy/create.rb +++ b/lib/factory_bot/strategy/create.rb @@ -6,6 +6,8 @@ def association(runner) end def result(evaluation) + evaluation.notify(:before_build, nil) + evaluation.object.tap do |instance| evaluation.notify(:after_build, instance) evaluation.notify(:before_create, instance) diff --git a/spec/acceptance/callbacks_spec.rb b/spec/acceptance/callbacks_spec.rb index 1891fe49e..16b970fda 100644 --- a/spec/acceptance/callbacks_spec.rb +++ b/spec/acceptance/callbacks_spec.rb @@ -71,6 +71,7 @@ FactoryBot.define do before(:all) { TestLog << "global before-all called" } after(:all) { TestLog << "global after-all called" } + before(:build) { TestLog << "global before-build called" } after(:build) { TestLog << "global after-build called" } before(:create) { TestLog << "global before-create called" } after(:create) { TestLog << "global after-create called" } @@ -78,6 +79,7 @@ factory :parent, class: :user do before(:all) { TestLog << "parent before-all called" } after(:all) { TestLog << "parent after-all called" } + before(:build) { TestLog << "parent before-build called" } after(:build) { TestLog << "parent after-build called" } before(:create) { TestLog << "parent before-create called" } after(:create) { TestLog << "parent after-create called" } @@ -85,6 +87,7 @@ trait :parent_trait_1 do before(:all) { TestLog << "parent-trait-1 before-all called" } after(:all) { TestLog << "parent-trait-1 after-all called" } + before(:build) { TestLog << "parent-trait-1 before-build called" } after(:build) { TestLog << "parent-trait-1 after-build called" } before(:create) { TestLog << "parent-trait-1 before-create called" } after(:create) { TestLog << "parent-trait-1 after-create called" } @@ -93,6 +96,7 @@ trait :parent_trait_2 do before(:all) { TestLog << "parent-trait-2 before-all called" } after(:all) { TestLog << "parent-trait-2 after-all called" } + before(:build) { TestLog << "parent-trait-2 before-build called" } after(:build) { TestLog << "parent-trait-2 after-build called" } before(:create) { TestLog << "parent-trait-2 before-create called" } after(:create) { TestLog << "parent-trait-2 after-create called" } @@ -103,12 +107,14 @@ before(:all) { TestLog << "child before-all called" } after(:create) { TestLog << "child after-create called" } after(:build) { TestLog << "child after-build called" } + before(:build) { TestLog << "child before-build called" } before(:create) { TestLog << "child before-create called" } after(:all) { TestLog << "child after-all called" } trait :child_trait do before(:all) { TestLog << "child-trait before-all called" } after(:all) { TestLog << "child-trait after-all called" } + before(:build) { TestLog << "child-trait before-build called" } after(:build) { TestLog << "child-trait after-build called" } before(:create) { TestLog << "child-trait before-create called" } after(:create) { TestLog << "child-trait after-create called" } @@ -125,7 +131,7 @@ # FactoryBot.create(:child, :parent_trait_2, :child_trait, :parent_trait_1) - expect(TestLog.size).to eq 30 + expect(TestLog.size).to eq 36 # before(:all) expect(TestLog[0..5]).to eq [ @@ -137,8 +143,18 @@ "parent-trait-1 before-all called" ] - # after(:build) + # before(:build) expect(TestLog[6..11]).to eq [ + "global before-build called", + "parent before-build called", + "child before-build called", + "parent-trait-2 before-build called", + "child-trait before-build called", + "parent-trait-1 before-build called" + ] + + # after(:build) + expect(TestLog[12..17]).to eq [ "global after-build called", "parent after-build called", "child after-build called", @@ -148,7 +164,7 @@ ] # before(:create) - expect(TestLog[12..17]).to eq [ + expect(TestLog[18..23]).to eq [ "global before-create called", "parent before-create called", "child before-create called", @@ -158,7 +174,7 @@ ] # after(:create) - expect(TestLog[18..23]).to eq [ + expect(TestLog[24..29]).to eq [ "global after-create called", "parent after-create called", "child after-create called", @@ -168,7 +184,7 @@ ] # after(:all) - expect(TestLog[24..29]).to eq [ + expect(TestLog[30..35]).to eq [ "global after-all called", "parent after-all called", "child after-all called", @@ -463,3 +479,33 @@ def name expect(build(:company).name).to eq "ACME SUPPLIERS" end end + +describe "before build callback" do + before do + define_class("TitleSetter") do + def self.title=(new_title) + class_variable_set(:@@title, new_title) + end + + def self.title + class_variable_get(:@@title) + end + end + + define_model("Article", title: :string) + + FactoryBot.define do + factory :article_with_before_callbacks, class: :article do + before(:build) { TitleSetter.title = "title from before build" } + after(:build) { TitleSetter.title = "title from after build" } + + title { TitleSetter.title } + end + end + end + + it "runs the before callback" do + article = FactoryBot.build(:article_with_before_callbacks) + expect(article.title).to eq("title from before build") + end +end