1
0
Fork 0
mirror of https://github.com/thoughtbot/factory_bot.git synced 2022-11-09 11:43:51 -05:00

Added the AttributeProxy class and an association method, and added functionality for using AttributeProxy from Factory. This allows lazy generation of associations using the appropriate method.

This commit is contained in:
Joe Ferris 2008-05-31 15:16:59 -07:00
parent 0a6a54c6b9
commit 7b3936b611
6 changed files with 172 additions and 16 deletions

35
README
View file

@ -21,6 +21,19 @@ factories there. This file can be included from test_helper or directly from
your test files. Don't forget:
require 'factory_girl'
== Lazy Attributes
Most attributes can be added using static values that are evaluated when the
factory is defined, but some attributes (such as associations and other
attributes that must be dynamically generated) will need values assigned each
time an instance is generated. These "lazy" attributes can be added by passing
a block instead of a parameter:
Factory.define :user do |u|
# ...
u.activation_code { User.generate_activation_code }
end
== Using factories
# Build and save a User instance
@ -31,3 +44,25 @@ your test files. Don't forget:
# Return an attributes Hash that can be used to build a User instance
attrs = Factory.attributes_for(:user)
== Associations
Associated instances can be generated by using the association method when
defining a lazy attribute:
Factory.define :post do |p|
# ...
p.author {|author| author.association(:user, :last_name => 'Writely') }
end
When using the association method, the same build strategy (build, create, or attributes_for) will be used for all generated instances:
# Builds and saves a User and a Post
post = Factory(:post)
post.new_record? # => false
post.author.new_record # => false
# Builds but does not save a User and a Post
Factory.build(:post)
post.new_record? # => true
post.author.new_record # => true

View file

@ -1,5 +1,6 @@
require 'activesupport'
require 'factory_girl/factory'
require 'factory_girl/attribute_proxy'
# Shortcut for Factory.create.
#

View file

@ -0,0 +1,39 @@
class Factory
class AttributeProxy
attr_accessor :factory, :attribute_name, :strategy #:nodoc:
def initialize (factory, attr, strategy) #:nodoc:
@factory = factory
@attribute_name = attr
@strategy = strategy
end
# Generates an association using the current build strategy.
#
# Example:
#
# Factory.define :user do |f|
# # ...
# end
#
# Factory.define :post do |f|
# # ...
# f.author {|a| a.association :user, :name => 'Joe' }
# end
#
# # Builds (but doesn't save) a Post and a User
# Factory.build(:post)
#
# # Builds and saves a User, builds a Post, assigns the User to the
# # author association, and saves the User.
# Factory.create(:post)
#
def association (name, attributes = {})
Factory.send(strategy, name, attributes)
end
end
end

View file

@ -5,8 +5,8 @@ class Factory
attr_reader :name
# Defines a new factory that can be used by the generation methods (create
# and build) to build new objects.
# Defines a new factory that can be used by the build strategies (create and
# build) to build new objects.
#
# Arguments:
# name: (Symbol)
@ -37,13 +37,18 @@ class Factory
@lazy_attributes = {}
end
# Adds an attribute that should be assigned on generated instances for this factory.
# Adds an attribute that should be assigned on generated instances for this
# factory.
#
# This method should be called with either a value or block, but not both. If
# called with a block, the attribute will be generated "lazily," whenever an
# instance is generated. Lazy attribute blocks will not be called if that
# attribute is overriden for a specific instance.
#
# When defining lazy attributes, an instance of Factory::AttributeProxy will
# be yielded, allowing associations to be built using the correct build
# strategy.
#
# Arguments:
# name: (Symbol)
# The name of this attribute. This will be assigned using :"#{name}=" for
@ -80,24 +85,15 @@ class Factory
end
def attributes_for (attrs = {}) #:nodoc:
result = {}
@lazy_attributes.each do |name, block|
result[name] = block.call unless attrs.key?(name)
end
result.update(@static_attributes)
result.update(attrs)
build_attributes_hash(attrs, :attributes_for)
end
def build (attrs = {}) #:nodoc:
instance = build_class.new
attributes_for(attrs).each do |attr, value|
instance.send(:"#{attr}=", value)
end
instance
build_instance(attrs, :build)
end
def create (attrs = {}) #:nodoc:
instance = build(attrs)
instance = build_instance(attrs, :create)
instance.save!
instance
end
@ -159,4 +155,25 @@ class Factory
end
private
def build_attributes_hash (override, strategy)
result = {}
@lazy_attributes.each do |name, block|
proxy = AttributeProxy.new(self, name, strategy)
result[name] = block.call(proxy) unless override.key?(name)
end
result.update(@static_attributes)
result.update(override)
end
def build_instance (override, strategy)
instance = build_class.new
attrs = build_attributes_hash(override, strategy)
attrs.each do |attr, value|
instance.send(:"#{attr}=", value)
end
instance
end
end

View file

@ -0,0 +1,49 @@
require(File.join(File.dirname(__FILE__), 'test_helper'))
class AttributeProxyTest < Test::Unit::TestCase
context "an association proxy" do
setup do
@factory = mock('factory')
@attr = :user
@strategy = :create
@proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy)
end
should "have a factory" do
assert_equal @factory, @proxy.factory
end
should "have an attribute name" do
assert_equal @attr, @proxy.attribute_name
end
should "have a build strategy" do
assert_equal @strategy, @proxy.strategy
end
context "building an association" do
setup do
@association = mock('built-user')
@name = :user
@attribs = { :first_name => 'Billy' }
Factory.stubs(@strategy).returns(@association)
end
should "delegate to the appropriate method on Factory" do
Factory.expects(@strategy).with(@name, @attribs).returns(@association)
@proxy.association(@name, @attribs)
end
should "return the built association" do
assert_equal @association, @proxy.association(@name)
end
end
end
end

View file

@ -85,7 +85,9 @@ class FactoryTest < Test::Unit::TestCase
context "when adding an attribute with a block" do
setup do
@attr = :name
@attr = :name
@proxy = mock('attr-proxy')
Factory::AttributeProxy.stubs(:new).returns(@proxy)
end
should "not evaluate the block when the attribute is added" do
@ -107,6 +109,19 @@ class FactoryTest < Test::Unit::TestCase
assert_equal value, @factory.attributes_for[@attr]
end
should "build an attribute proxy" do
Factory::AttributeProxy.expects(:new).with(@factory, @attr, :attributes_for)
@factory.add_attribute(@attr) {}
@factory.attributes_for
end
should "yield an attribute proxy to the block" do
yielded = nil
@factory.add_attribute(@attr) {|y| yielded = y }
@factory.attributes_for
assert_equal @proxy, yielded
end
end
should "add an attribute using the method name when passed an undefined method" do