Introduce new callback syntax
Instead of calling before_create, after_build, after_create, or after_stub, you can now call: before(:create) {|instance| instance.name = "overridden!" } after(:create) {|instance| instance.name = "overridden!" } after(:build) {|instance| instance.name = "overridden!" } after(:stub) {|instance| instance.name = "overridden!" } Additionally, you can declare callbacks longhand: callback(:after_stub) {|instance| instance.name = "overridden!" } This allows for custom callbacks to be defined: callback(:custom_callback) {|instance| instance.name = "overridden!" } Which can then be used from a custom strategy: class CustomStrategy def association(runner); end def result(evaluation) evaluation.object.tap do |instance| evaluation.notify(:custom_callback, instance) end end end FactoryGirl.register_strategy(:custom, CustomStrategy) This would allow for calling: FactoryGirl.custom(:user) Which would return the user instance but execute the :custom_callback callback on the user instance first.
This commit is contained in:
parent
95a4626daa
commit
bef5a01b31
|
@ -207,7 +207,7 @@ factory :user do
|
||||||
name { "John Doe#{" - Rockstar" if rockstar}" }
|
name { "John Doe#{" - Rockstar" if rockstar}" }
|
||||||
email { "#{name.downcase}@example.com" }
|
email { "#{name.downcase}@example.com" }
|
||||||
|
|
||||||
after_create do |user, evaluator|
|
after(:create) do |user, evaluator|
|
||||||
user.name.upcase! if evaluator.upcased
|
user.name.upcase! if evaluator.upcased
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -299,11 +299,11 @@ FactoryGirl.define do
|
||||||
posts_count 5
|
posts_count 5
|
||||||
end
|
end
|
||||||
|
|
||||||
# the after_create yields two values; the user instance itself and the
|
# the after(:create) yields two values; the user instance itself and the
|
||||||
# evaluator, which stores all values from the factory, including ignored
|
# evaluator, which stores all values from the factory, including ignored
|
||||||
# attributes; `create_list`'s second argument is the number of records
|
# attributes; `create_list`'s second argument is the number of records
|
||||||
# to create and we make sure the user is associated properly to the post
|
# to create and we make sure the user is associated properly to the post
|
||||||
after_create do |user, evaluator|
|
after(:create) do |user, evaluator|
|
||||||
FactoryGirl.create_list(:post, evaluator.posts_count, user: user)
|
FactoryGirl.create_list(:post, evaluator.posts_count, user: user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -589,18 +589,19 @@ FactoryGirl.create_list(:user, 3, :admin, :male, name: "Jon Snow")
|
||||||
Callbacks
|
Callbacks
|
||||||
---------
|
---------
|
||||||
|
|
||||||
factory\_girl makes available three callbacks for injecting some code:
|
factory\_girl makes available four callbacks for injecting some code:
|
||||||
|
|
||||||
* after_build - called after a factory is built (via FactoryGirl.build)
|
* after(:build) - called after a factory is built (via `FactoryGirl.build`, `FactoryGirl.create`)
|
||||||
* after_create - called after a factory is saved (via FactoryGirl.create)
|
* before(:create) - called before a factory is saved (via `FactoryGirl.create`)
|
||||||
* after_stub - called after a factory is stubbed (via FactoryGirl.build_stubbed)
|
* after(:create) - called after a factory is saved (via `FactoryGirl.create`)
|
||||||
|
* after(:stub) - called after a factory is stubbed (via `FactoryGirl.build_stubbed`)
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
# Define a factory that calls the generate_hashed_password method after it is built
|
# Define a factory that calls the generate_hashed_password method after it is built
|
||||||
factory :user do
|
factory :user do
|
||||||
after_build { |user| generate_hashed_password(user) }
|
after(:build) { |user| generate_hashed_password(user) }
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -610,8 +611,8 @@ You can also define multiple types of callbacks on the same factory:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
factory :user do
|
factory :user do
|
||||||
after_build { |user| do_something_to(user) }
|
after(:build) { |user| do_something_to(user) }
|
||||||
after_create { |user| do_something_else_to(user) }
|
after(:create) { |user| do_something_else_to(user) }
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -619,12 +620,12 @@ Factories can also define any number of the same kind of callback. These callba
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
factory :user do
|
factory :user do
|
||||||
after_create { this_runs_first }
|
after(:create) { this_runs_first }
|
||||||
after_create { then_this }
|
after(:create) { then_this }
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Calling FactoryGirl.create will invoke both after\_build and after\_create callbacks.
|
Calling FactoryGirl.create will invoke both `after_build` and `after_create` callbacks.
|
||||||
|
|
||||||
Also, like standard attributes, child factories will inherit (and can also define) callbacks from their parent factory.
|
Also, like standard attributes, child factories will inherit (and can also define) callbacks from their parent factory.
|
||||||
|
|
||||||
|
@ -677,7 +678,7 @@ When modifying a factory, you can change any of the attributes you want (aside f
|
||||||
`FactoryGirl.modify` must be called outside of a `FactoryGirl.define` block as it operates on factories differently.
|
`FactoryGirl.modify` must be called outside of a `FactoryGirl.define` block as it operates on factories differently.
|
||||||
|
|
||||||
A caveat: you can only modify factories (not sequences or traits) and callbacks *still compound as they normally would*. So, if
|
A caveat: you can only modify factories (not sequences or traits) and callbacks *still compound as they normally would*. So, if
|
||||||
the factory you're modifying defines an `after_create` callback, you defining an `after_create` won't override it, it'll just get run after the first callback.
|
the factory you're modifying defines an `after(:create)` callback, you defining an `after(:create)` won't override it, it'll just get run after the first callback.
|
||||||
|
|
||||||
Building or Creating Multiple Records
|
Building or Creating Multiple Records
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
@ -810,6 +811,41 @@ FactoryGirl.json(:user)
|
||||||
Finally, you can override factory\_girl's own strategies if you'd like by
|
Finally, you can override factory\_girl's own strategies if you'd like by
|
||||||
registering a new object in place of the strategies.
|
registering a new object in place of the strategies.
|
||||||
|
|
||||||
|
Custom Callbacks
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Custom callbacks can be defined if you're using custom strategies:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class JsonStrategy
|
||||||
|
def initialize
|
||||||
|
@strategy = FactoryGirl.strategy_by_name(:create).new
|
||||||
|
end
|
||||||
|
|
||||||
|
delegate :association, to: :@strategy
|
||||||
|
|
||||||
|
def result(evaluation)
|
||||||
|
result = @strategy.result(evaluation)
|
||||||
|
evaluation.notify(:before_json, result)
|
||||||
|
|
||||||
|
result.to_json.tap do |json|
|
||||||
|
evaluation.notify(:after_json, json)
|
||||||
|
evaluation.notify(:make_json_awesome, json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
FactoryGirl.register_strategy(:json, JsonStrategy)
|
||||||
|
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :user do
|
||||||
|
before(:json) {|user| do_something_to(user) }
|
||||||
|
after(:json) {|user_json| do_something_to(user_json) }
|
||||||
|
callback(:make_json_awesome) {|user_json| do_something_to(user_json) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
Custom Methods to Persist Objects
|
Custom Methods to Persist Objects
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
require "set"
|
||||||
require "active_support/core_ext/module/delegation"
|
require "active_support/core_ext/module/delegation"
|
||||||
require "active_support/notifications"
|
require "active_support/notifications"
|
||||||
|
|
||||||
|
@ -103,9 +104,26 @@ module FactoryGirl
|
||||||
FactoryGirl.register_strategy(:null, FactoryGirl::Strategy::Null)
|
FactoryGirl.register_strategy(:null, FactoryGirl::Strategy::Null)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.callbacks
|
||||||
|
@callbacks ||= Set.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.register_default_callbacks
|
||||||
|
register_callback(:after_build)
|
||||||
|
register_callback(:after_create)
|
||||||
|
register_callback(:after_stub)
|
||||||
|
register_callback(:before_create)
|
||||||
|
end
|
||||||
|
|
||||||
def self.callback_names
|
def self.callback_names
|
||||||
[:after_build, :after_create, :after_stub, :before_create].freeze
|
callbacks
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.register_callback(name)
|
||||||
|
name = name.to_sym
|
||||||
|
callbacks << name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
FactoryGirl.register_default_strategies
|
FactoryGirl.register_default_strategies
|
||||||
|
FactoryGirl.register_default_callbacks
|
||||||
|
|
|
@ -159,5 +159,18 @@ module FactoryGirl
|
||||||
def initialize_with(&block)
|
def initialize_with(&block)
|
||||||
@definition.define_constructor(&block)
|
@definition.define_constructor(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def before(name, &block)
|
||||||
|
callback("before_#{name}", &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def after(name, &block)
|
||||||
|
callback("after_#{name}", &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def callback(name, &block)
|
||||||
|
FactoryGirl.register_callback(name)
|
||||||
|
@definition.add_callback(Callback.new(name, block))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe "using ActiveSupport::Instrumentation to track factory interaction" do
|
||||||
email "john@example.com"
|
email "john@example.com"
|
||||||
|
|
||||||
factory :slow_user do
|
factory :slow_user do
|
||||||
after_build { Kernel.sleep(0.1) }
|
after(:build) { Kernel.sleep(0.1) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,35 +6,35 @@ describe "callbacks" do
|
||||||
|
|
||||||
FactoryGirl.define do
|
FactoryGirl.define do
|
||||||
factory :user_with_callbacks, class: :user do
|
factory :user_with_callbacks, class: :user do
|
||||||
after_stub { |user| user.first_name = 'Stubby' }
|
after(:stub) { |user| user.first_name = 'Stubby' }
|
||||||
after_build { |user| user.first_name = 'Buildy' }
|
after(:build) { |user| user.first_name = 'Buildy' }
|
||||||
after_create { |user| user.last_name = 'Createy' }
|
after(:create) { |user| user.last_name = 'Createy' }
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :user_with_inherited_callbacks, parent: :user_with_callbacks do
|
factory :user_with_inherited_callbacks, parent: :user_with_callbacks do
|
||||||
after_stub { |user| user.last_name = 'Double-Stubby' }
|
after(:stub) { |user| user.last_name = 'Double-Stubby' }
|
||||||
after_build { |user| user.first_name = 'Child-Buildy' }
|
after(:build) { |user| user.first_name = 'Child-Buildy' }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "runs the after_stub callback when stubbing" do
|
it "runs the after(:stub) callback when stubbing" do
|
||||||
user = FactoryGirl.build_stubbed(:user_with_callbacks)
|
user = FactoryGirl.build_stubbed(:user_with_callbacks)
|
||||||
user.first_name.should == 'Stubby'
|
user.first_name.should == 'Stubby'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "runs the after_build callback when building" do
|
it "runs the after(:build) callback when building" do
|
||||||
user = FactoryGirl.build(:user_with_callbacks)
|
user = FactoryGirl.build(:user_with_callbacks)
|
||||||
user.first_name.should == 'Buildy'
|
user.first_name.should == 'Buildy'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "runs both the after_build and after_create callbacks when creating" do
|
it "runs both the after(:build) and after(:create) callbacks when creating" do
|
||||||
user = FactoryGirl.create(:user_with_callbacks)
|
user = FactoryGirl.create(:user_with_callbacks)
|
||||||
user.first_name.should == 'Buildy'
|
user.first_name.should == 'Buildy'
|
||||||
user.last_name.should == 'Createy'
|
user.last_name.should == 'Createy'
|
||||||
end
|
end
|
||||||
|
|
||||||
it "runs both the after_stub callback on the factory and the inherited after_stub callback" do
|
it "runs both the after(:stub) callback on the factory and the inherited after(:stub) callback" do
|
||||||
user = FactoryGirl.build_stubbed(:user_with_inherited_callbacks)
|
user = FactoryGirl.build_stubbed(:user_with_inherited_callbacks)
|
||||||
user.first_name.should == 'Stubby'
|
user.first_name.should == 'Stubby'
|
||||||
user.last_name.should == 'Double-Stubby'
|
user.last_name.should == 'Double-Stubby'
|
||||||
|
@ -56,9 +56,9 @@ describe "callbacks using syntax methods without referencing FactoryGirl explici
|
||||||
sequence(:sequence_3)
|
sequence(:sequence_3)
|
||||||
|
|
||||||
factory :user do
|
factory :user do
|
||||||
after_stub { generate(:sequence_3) }
|
after(:stub) { generate(:sequence_3) }
|
||||||
after_build {|user| user.first_name = generate(:sequence_1) }
|
after(:build) {|user| user.first_name = generate(:sequence_1) }
|
||||||
after_create {|user, evaluator| user.last_name = generate(:sequence_2) }
|
after(:create) {|user, evaluator| user.last_name = generate(:sequence_2) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -76,3 +76,76 @@ describe "callbacks using syntax methods without referencing FactoryGirl explici
|
||||||
FactoryGirl.create(:user).last_name.should == 1
|
FactoryGirl.create(:user).last_name.should == 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "custom callbacks" do
|
||||||
|
let(:custom_before) do
|
||||||
|
Class.new do
|
||||||
|
def result(evaluation)
|
||||||
|
evaluation.object.tap do |instance|
|
||||||
|
evaluation.notify(:before_custom, instance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:custom_after) do
|
||||||
|
Class.new do
|
||||||
|
def result(evaluation)
|
||||||
|
evaluation.object.tap do |instance|
|
||||||
|
evaluation.notify(:after_custom, instance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:totally_custom) do
|
||||||
|
Class.new do
|
||||||
|
def result(evaluation)
|
||||||
|
evaluation.object.tap do |instance|
|
||||||
|
evaluation.notify(:totally_custom, instance)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
define_model("User", first_name: :string, last_name: :string) do
|
||||||
|
def name
|
||||||
|
[first_name, last_name].join(" ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
FactoryGirl.register_strategy(:custom_before, custom_before)
|
||||||
|
FactoryGirl.register_strategy(:custom_after, custom_after)
|
||||||
|
FactoryGirl.register_strategy(:totally_custom, totally_custom)
|
||||||
|
|
||||||
|
FactoryGirl.define do
|
||||||
|
factory :user do
|
||||||
|
first_name "John"
|
||||||
|
last_name "Doe"
|
||||||
|
|
||||||
|
before(:custom) {|instance| instance.first_name = "Overridden First" }
|
||||||
|
after(:custom) {|instance| instance.last_name = "Overridden Last" }
|
||||||
|
callback(:totally_custom) do |instance|
|
||||||
|
instance.first_name = "Totally"
|
||||||
|
instance.last_name = "Custom"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "runs a custom before callback when the proper strategy executes" do
|
||||||
|
FactoryGirl.build(:user).name.should == "John Doe"
|
||||||
|
FactoryGirl.custom_before(:user).name.should == "Overridden First Doe"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "runs a custom after callback when the proper strategy executes" do
|
||||||
|
FactoryGirl.build(:user).name.should == "John Doe"
|
||||||
|
FactoryGirl.custom_after(:user).name.should == "John Overridden Last"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "runs a custom callback without prepending before or after when the proper strategy executes" do
|
||||||
|
FactoryGirl.build(:user).name.should == "John Doe"
|
||||||
|
FactoryGirl.totally_custom(:user).name.should == "Totally Custom"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -64,7 +64,7 @@ describe "multiple creates and ignored attributes to dynamically build attribute
|
||||||
posts_count 5
|
posts_count 5
|
||||||
end
|
end
|
||||||
|
|
||||||
after_create do |user, evaluator|
|
after(:create) do |user, evaluator|
|
||||||
FactoryGirl.create_list(:post, evaluator.posts_count, user: user)
|
FactoryGirl.create_list(:post, evaluator.posts_count, user: user)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ describe "modifying factories" do
|
||||||
factory :user do
|
factory :user do
|
||||||
email
|
email
|
||||||
|
|
||||||
after_create do |user|
|
after(:create) do |user|
|
||||||
user.login = user.name.upcase if user.name
|
user.login = user.name.upcase if user.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ describe "modifying factories" do
|
||||||
FactoryGirl.modify do
|
FactoryGirl.modify do
|
||||||
factory :user do
|
factory :user do
|
||||||
name "Great User"
|
name "Great User"
|
||||||
after_create do |user|
|
after(:create) do |user|
|
||||||
user.name = user.name.downcase
|
user.name = user.name.downcase
|
||||||
user.login = nil
|
user.login = nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -78,7 +78,7 @@ describe "nested factories with different parents" do
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :uppercase_male_user, parent: :male_user do
|
factory :uppercase_male_user, parent: :male_user do
|
||||||
after_build {|user| user.name = user.name.upcase }
|
after(:build) {|user| user.name = user.name.upcase }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -187,11 +187,11 @@ describe "traits with callbacks" do
|
||||||
name "John"
|
name "John"
|
||||||
|
|
||||||
trait :great do
|
trait :great do
|
||||||
after_create {|user| user.name.upcase! }
|
after(:create) {|user| user.name.upcase! }
|
||||||
end
|
end
|
||||||
|
|
||||||
trait :awesome do
|
trait :awesome do
|
||||||
after_create {|user| user.name = "awesome" }
|
after(:create) {|user| user.name = "awesome" }
|
||||||
end
|
end
|
||||||
|
|
||||||
factory :caps_user, traits: [:great]
|
factory :caps_user, traits: [:great]
|
||||||
|
@ -232,7 +232,7 @@ describe "traits added via strategy" do
|
||||||
end
|
end
|
||||||
|
|
||||||
trait :great do
|
trait :great do
|
||||||
after_create {|user| user.name.upcase! }
|
after(:create) {|user| user.name.upcase! }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ describe "transient attributes" do
|
||||||
name { "#{FactoryGirl.generate(:name)}#{" - Rockstar" if rockstar}" }
|
name { "#{FactoryGirl.generate(:name)}#{" - Rockstar" if rockstar}" }
|
||||||
email { "#{name.downcase}#{four}@example.com" }
|
email { "#{name.downcase}#{four}@example.com" }
|
||||||
|
|
||||||
after_create do |user, evaluator|
|
after(:create) do |user, evaluator|
|
||||||
user.name.upcase! if evaluator.upcased
|
user.name.upcase! if evaluator.upcased
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -126,18 +126,18 @@ describe FactoryGirl::DefinitionProxy, "adding callbacks" do
|
||||||
let(:proxy) { FactoryGirl::DefinitionProxy.new(subject) }
|
let(:proxy) { FactoryGirl::DefinitionProxy.new(subject) }
|
||||||
let(:callback) { -> { "my awesome callback!" } }
|
let(:callback) { -> { "my awesome callback!" } }
|
||||||
|
|
||||||
context "#after_build" do
|
context "#after(:build)" do
|
||||||
before { proxy.after_build(&callback) }
|
before { proxy.after(:build, &callback) }
|
||||||
it { should have_callback(:after_build).with_block(callback) }
|
it { should have_callback(:after_build).with_block(callback) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#after_create" do
|
context "#after(:create)" do
|
||||||
before { proxy.after_create(&callback) }
|
before { proxy.after(:create, &callback) }
|
||||||
it { should have_callback(:after_create).with_block(callback) }
|
it { should have_callback(:after_create).with_block(callback) }
|
||||||
end
|
end
|
||||||
|
|
||||||
context "#after_stub" do
|
context "#after(:stub)" do
|
||||||
before { proxy.after_stub(&callback) }
|
before { proxy.after(:stub, &callback) }
|
||||||
it { should have_callback(:after_stub).with_block(callback) }
|
it { should have_callback(:after_stub).with_block(callback) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue