Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 42 additions & 25 deletions docs/src/callbacks/callback_order.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)"

```

Expand All @@ -49,53 +55,64 @@ 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
end
end
end

build(:child, :trait_c, :trait_a, :trait_b)
build(:child, :trait_c, :trait_a, :trait_b)

# Result:
#
# 1. "Global before(:all)"
# 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)"
```
11 changes: 7 additions & 4 deletions docs/src/callbacks/default-callbacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
11 changes: 6 additions & 5 deletions docs/src/callbacks/summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@ 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

### 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.
#
Expand All @@ -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
```
```
2 changes: 2 additions & 0 deletions lib/factory_bot/strategy/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ def association(runner)
end

def result(evaluation)
evaluation.notify(:before_build, nil)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added only to the strategies that also trigger after_build, skipping build_stubbed (debatable) and attributes_for


evaluation.object.tap do |instance|
evaluation.notify(:after_build, instance)
end
Expand Down
2 changes: 2 additions & 0 deletions lib/factory_bot/strategy/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
56 changes: 51 additions & 5 deletions spec/acceptance/callbacks_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -71,20 +71,23 @@
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" }

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

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" }
Expand All @@ -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" }
Expand All @@ -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" }
Expand All @@ -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 [
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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