Skip to content

Commit

Permalink
New default syntax for using defined factories
Browse files Browse the repository at this point in the history
  • Loading branch information
jferris committed Jan 20, 2011
1 parent fc6d1b3 commit 7c90e9d
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 112 deletions.
63 changes: 32 additions & 31 deletions README.md
Expand Up @@ -67,38 +67,36 @@ Using factories
factory_girl supports several different build strategies: build, create, attributes_for and stub: factory_girl supports several different build strategies: build, create, attributes_for and stub:


# Returns a User instance that's not saved # Returns a User instance that's not saved
user = Factory.build(:user) user = FactoryGirl.build(:user)


# Returns a saved User instance # Returns a saved User instance
user = Factory.create(:user) user = FactoryGirl.create(:user)


# Returns a hash of attributes that can be used to build a User instance: # Returns a hash of attributes that can be used to build a User instance:
attrs = Factory.attributes_for(:user) attrs = FactoryGirl.attributes_for(:user)


# Returns an object with all defined attributes stubbed out: # Returns an object with all defined attributes stubbed out:
stub = Factory.stub(:user) stub = FactoryGirl.stub(:user)

You can use the Factory method as a shortcut for the default build strategy:

# Same as Factory.create :user:
user = Factory(:user)

The default strategy can be overriden:

# Now same as Factory.build(:user)
factory :user, :default_strategy => :build do
...
end

user = Factory(:user)


No matter which strategy is used, it's possible to override the defined attributes by passing a hash: No matter which strategy is used, it's possible to override the defined attributes by passing a hash:


# Build a User instance and override the first_name property # Build a User instance and override the first_name property
user = Factory.build(:user, :first_name => 'Joe') user = FactoryGirl.build(:user, :first_name => 'Joe')
user.first_name user.first_name
# => "Joe" # => "Joe"


If repeating "FactoryGirl" is too verbose for you, you can mix the syntax methods in:

# rspec
RSpec.configure do |config|
config.include Factory::Syntax::Methods
end

# Test::Unit
class Test::Unit::TestCase
include Factory::Syntax::Methods
end

Lazy Attributes Lazy Attributes
--------------- ---------------


Expand All @@ -119,8 +117,8 @@ Attributes can be based on the values of other attributes using the proxy that i
last_name 'Blow' last_name 'Blow'
email { "#{first_name}.#{last_name}@example.com".downcase } email { "#{first_name}.#{last_name}@example.com".downcase }
end end

Factory(:user, :last_name => 'Doe').email FactoryGirl.create(:user, :last_name => 'Doe').email
# => "joe.doe@example.com" # => "joe.doe@example.com"


Associations Associations
Expand All @@ -144,12 +142,12 @@ You can also specify a different factory or override attributes:
The behavior of the association method varies depending on the build strategy used for the parent object. The behavior of the association method varies depending on the build strategy used for the parent object.


# Builds and saves a User and a Post # Builds and saves a User and a Post
post = Factory(:post) post = FactoryGirl.create(:post)
post.new_record? # => false post.new_record? # => false
post.author.new_record # => false post.author.new_record # => false


# Builds and saves a User, and then builds but does not save a Post # Builds and saves a User, and then builds but does not save a Post
post = Factory.build(:post) post = FactoryGirl.build(:post)
post.new_record? # => true post.new_record? # => true
post.author.new_record # => false post.author.new_record # => false


Expand All @@ -176,12 +174,15 @@ Sequences
--------- ---------


Unique values in a specific format (for example, e-mail addresses) can be Unique values in a specific format (for example, e-mail addresses) can be
generated using sequences. Sequences are defined by calling Factory.sequence, generated using sequences. Sequences are defined by calling sequence in a
and values in a sequence are generated by calling Factory.next: definition block, and values in a sequence are generated by calling
Factory.next:


# Defines a new sequence # Defines a new sequence
FactoryGirl.sequence :email do |n| FactoryGirl.define do
"person#{n}@example.com" sequence :email do |n|
"person#{n}@example.com"
end
end end


Factory.next :email Factory.next :email
Expand Down Expand Up @@ -214,9 +215,9 @@ Callbacks


Factory_girl makes available three callbacks for injecting some code: Factory_girl makes available three callbacks for injecting some code:


* after_build - called after a factory is built (via Factory.build) * after_build - called after a factory is built (via FactoryGirl.build)
* after_create - called after a factory is saved (via Factory.create) * after_create - called after a factory is saved (via FactoryGirl.create)
* after_stub - called after a factory is stubbed (via Factory.stub) * after_stub - called after a factory is stubbed (via FactoryGirl.stub)


Examples: Examples:


Expand All @@ -241,7 +242,7 @@ Factories can also define any number of the same kind of callback. These callba
after_create { then_this } after_create { then_this }
end end


Calling Factory.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 define additional) callbacks from their parent factory. Also, like standard attributes, child factories will inherit (and can define additional) callbacks from their parent factory.


Expand Down
1 change: 1 addition & 0 deletions lib/factory_girl.rb
Expand Up @@ -12,6 +12,7 @@
require 'factory_girl/sequence' require 'factory_girl/sequence'
require 'factory_girl/aliases' require 'factory_girl/aliases'
require 'factory_girl/definition_proxy' require 'factory_girl/definition_proxy'
require 'factory_girl/syntax/methods'
require 'factory_girl/syntax/default' require 'factory_girl/syntax/default'
require 'factory_girl/syntax/vintage' require 'factory_girl/syntax/vintage'
require 'factory_girl/find_definitions' require 'factory_girl/find_definitions'
Expand Down
2 changes: 2 additions & 0 deletions lib/factory_girl/syntax/default.rb
@@ -1,6 +1,8 @@
module FactoryGirl module FactoryGirl
module Syntax module Syntax
module Default module Default
include Methods

def define(&block) def define(&block)
DSL.run(block) DSL.run(block)
end end
Expand Down
75 changes: 75 additions & 0 deletions lib/factory_girl/syntax/methods.rb
@@ -0,0 +1,75 @@
module FactoryGirl
module Syntax
module Methods
# Generates and returns a Hash of attributes from this factory. Attributes
# can be individually overridden by passing in a Hash of attribute => value
# pairs.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this set.
#
# Returns: +Hash+
# A set of attributes that can be used to build an instance of the class
# this factory generates.
def attributes_for(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::AttributesFor, overrides)
end

# Generates and returns an instance from this factory. Attributes can be
# individually overridden by passing in a Hash of attribute => value pairs.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# An instance of the class this factory generates, with generated attributes
# assigned.
def build(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::Build, overrides)
end

# Generates, saves, and returns an instance from this factory. Attributes can
# be individually overridden by passing in a Hash of attribute => value
# pairs.
#
# Instances are saved using the +save!+ method, so ActiveRecord models will
# raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# A saved instance of the class this factory generates, with generated
# attributes assigned.
def create(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::Create, overrides)
end

# Generates and returns an object with all attributes from this factory
# stubbed out. Attributes can be individually overridden by passing in a Hash
# of attribute => value pairs.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# An object with generated attributes stubbed out.
def build_stubbed(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::Stub, overrides)
end

end
end
end
76 changes: 7 additions & 69 deletions lib/factory_girl/syntax/vintage.rb
Expand Up @@ -2,6 +2,8 @@ module FactoryGirl
module Syntax module Syntax
module Vintage module Vintage
module Factory module Factory
extend Syntax::Methods

# Defines a new factory that can be used by the build strategies (create and # Defines a new factory that can be used by the build strategies (create and
# build) to build new objects. # build) to build new objects.
# #
Expand Down Expand Up @@ -33,75 +35,6 @@ def self.define(name, options = {})
FactoryGirl.register_factory(factory) FactoryGirl.register_factory(factory)
end end


# Generates and returns a Hash of attributes from this factory. Attributes
# can be individually overridden by passing in a Hash of attribute => value
# pairs.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this set.
#
# Returns: +Hash+
# A set of attributes that can be used to build an instance of the class
# this factory generates.
def self.attributes_for(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::AttributesFor, overrides)
end

# Generates and returns an instance from this factory. Attributes can be
# individually overridden by passing in a Hash of attribute => value pairs.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# An instance of the class this factory generates, with generated attributes
# assigned.
def self.build(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::Build, overrides)
end

# Generates, saves, and returns an instance from this factory. Attributes can
# be individually overridden by passing in a Hash of attribute => value
# pairs.
#
# Instances are saved using the +save!+ method, so ActiveRecord models will
# raise ActiveRecord::RecordInvalid exceptions for invalid attribute sets.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# A saved instance of the class this factory generates, with generated
# attributes assigned.
def self.create(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::Create, overrides)
end

# Generates and returns an object with all attributes from this factory
# stubbed out. Attributes can be individually overridden by passing in a Hash
# of attribute => value pairs.
#
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
# * overrides: +Hash+
# Attributes to overwrite for this instance.
#
# Returns: +Object+
# An object with generated attributes stubbed out.
def self.stub(name, overrides = {})
FactoryGirl.factory_by_name(name).run(Proxy::Stub, overrides)
end

# Executes the default strategy for the given factory. This is usually create, # Executes the default strategy for the given factory. This is usually create,
# but it can be overridden for each factory. # but it can be overridden for each factory.
# #
Expand Down Expand Up @@ -180,6 +113,11 @@ def self.alias(pattern, replace)
FactoryGirl.aliases << [pattern, replace] FactoryGirl.aliases << [pattern, replace]
end end


# Alias for build_stubbed. Deprecated.
def self.stub(name, overrides = {})
build_stubbed(name, overrides)
end

end end


# Shortcut for Factory.default_strategy. # Shortcut for Factory.default_strategy.
Expand Down
2 changes: 1 addition & 1 deletion spec/acceptance/attribute_aliases_spec.rb
Expand Up @@ -20,7 +20,7 @@
end end


it "doesn't assign both an association and its foreign key" do it "doesn't assign both an association and its foreign key" do
Factory.build(:post, :user_id => 1).user_id.should == 1 FactoryGirl.build(:post, :user_id => 1).user_id.should == 1
end end
end end


4 changes: 3 additions & 1 deletion spec/acceptance/attributes_for_spec.rb
Expand Up @@ -2,6 +2,8 @@
require 'acceptance/acceptance_helper' require 'acceptance/acceptance_helper'


describe "a generated attributes hash" do describe "a generated attributes hash" do
include FactoryGirl::Syntax::Methods

before do before do
define_model('User') define_model('User')


Expand All @@ -25,7 +27,7 @@
end end
end end


subject { Factory.attributes_for(:post, :title => 'overridden title') } subject { attributes_for(:post, :title => 'overridden title') }


it "assigns an overridden value" do it "assigns an overridden value" do
subject[:title].should == "overridden title" subject[:title].should == "overridden title"
Expand Down
4 changes: 3 additions & 1 deletion spec/acceptance/build_spec.rb
Expand Up @@ -2,6 +2,8 @@
require 'acceptance/acceptance_helper' require 'acceptance/acceptance_helper'


describe "a built instance" do describe "a built instance" do
include FactoryGirl::Syntax::Methods

before do before do
define_model('User') define_model('User')


Expand All @@ -19,7 +21,7 @@
end end
end end


subject { Factory.build(:post) } subject { build(:post) }


it "isn't saved" do it "isn't saved" do
should be_new_record should be_new_record
Expand Down
Expand Up @@ -2,6 +2,8 @@
require 'acceptance/acceptance_helper' require 'acceptance/acceptance_helper'


describe "a generated stub instance" do describe "a generated stub instance" do
include FactoryGirl::Syntax::Methods

before do before do
define_model('User') define_model('User')


Expand All @@ -23,7 +25,7 @@
end end
end end


subject { Factory.stub(:post, :title => 'overridden title') } subject { build_stubbed(:post, :title => 'overridden title') }


it "assigns a default attribute" do it "assigns a default attribute" do
subject.body.should == 'default body' subject.body.should == 'default body'
Expand All @@ -42,7 +44,7 @@
end end


it "generates unique ids" do it "generates unique ids" do
other_stub = Factory.stub(:post) other_stub = build_stubbed(:post)
subject.id.should_not == other_stub.id subject.id.should_not == other_stub.id
end end


Expand Down

0 comments on commit 7c90e9d

Please sign in to comment.