mirror of
https://github.com/thoughtbot/factory_bot.git
synced 2022-11-09 11:43:51 -05:00
Extracted yielded object for dsl into a proxy; moved default public api into its own file
This commit is contained in:
parent
eb8b774148
commit
ac8875c818
12 changed files with 842 additions and 798 deletions
|
@ -11,6 +11,8 @@ require 'factory_girl/attribute/association'
|
|||
require 'factory_girl/attribute/callback'
|
||||
require 'factory_girl/sequence'
|
||||
require 'factory_girl/aliases'
|
||||
require 'factory_girl/definition_proxy'
|
||||
require 'factory_girl/syntax/default'
|
||||
|
||||
# Shortcut for Factory.default_strategy.
|
||||
#
|
||||
|
|
122
lib/factory_girl/definition_proxy.rb
Normal file
122
lib/factory_girl/definition_proxy.rb
Normal file
|
@ -0,0 +1,122 @@
|
|||
class Factory
|
||||
class DefinitionProxy
|
||||
instance_methods.each do |method|
|
||||
undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$|^instance_eval$)/
|
||||
end
|
||||
|
||||
def initialize(factory)
|
||||
@factory = factory
|
||||
end
|
||||
|
||||
# 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 overridden for a specific instance.
|
||||
#
|
||||
# When defining lazy attributes, an instance of Factory::Proxy will
|
||||
# be yielded, allowing associations to be built using the correct build
|
||||
# strategy.
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+ or +String+
|
||||
# The name of this attribute. This will be assigned using :"#{name}=" for
|
||||
# generated instances.
|
||||
# * value: +Object+
|
||||
# If no block is given, this value will be used for this attribute.
|
||||
def add_attribute (name, value = nil, &block)
|
||||
if block_given?
|
||||
if value
|
||||
raise AttributeDefinitionError, "Both value and block given"
|
||||
else
|
||||
attribute = Attribute::Dynamic.new(name, block)
|
||||
end
|
||||
else
|
||||
attribute = Attribute::Static.new(name, value)
|
||||
end
|
||||
|
||||
@factory.define_attribute(attribute)
|
||||
end
|
||||
|
||||
# Calls add_attribute using the missing method name as the name of the
|
||||
# attribute, so that:
|
||||
#
|
||||
# Factory.define :user do |f|
|
||||
# f.name 'Billy Idol'
|
||||
# end
|
||||
#
|
||||
# and:
|
||||
#
|
||||
# Factory.define :user do |f|
|
||||
# f.add_attribute :name, 'Billy Idol'
|
||||
# end
|
||||
#
|
||||
# are equivilent.
|
||||
def method_missing (name, *args, &block)
|
||||
add_attribute(name, *args, &block)
|
||||
end
|
||||
|
||||
# Adds an attribute that will have unique values generated by a sequence with
|
||||
# a specified format.
|
||||
#
|
||||
# The result of:
|
||||
# Factory.define :user do |f|
|
||||
# f.sequence(:email) { |n| "person#{n}@example.com" }
|
||||
# end
|
||||
#
|
||||
# Is equal to:
|
||||
# Factory.sequence(:email) { |n| "person#{n}@example.com" }
|
||||
#
|
||||
# Factory.define :user do |f|
|
||||
# f.email { Factory.next(:email) }
|
||||
# end
|
||||
#
|
||||
# Except that no globally available sequence will be defined.
|
||||
def sequence(name, &block)
|
||||
sequence = Sequence.new(&block)
|
||||
add_attribute(name) { sequence.next }
|
||||
end
|
||||
|
||||
# Adds an attribute that builds an association. The associated instance will
|
||||
# be built using the same build strategy as the parent instance.
|
||||
#
|
||||
# Example:
|
||||
# Factory.define :user do |f|
|
||||
# f.name 'Joey'
|
||||
# end
|
||||
#
|
||||
# Factory.define :post do |f|
|
||||
# f.association :author, :factory => :user
|
||||
# end
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+
|
||||
# The name of this attribute.
|
||||
# * options: +Hash+
|
||||
#
|
||||
# Options:
|
||||
# * factory: +Symbol+ or +String+
|
||||
# The name of the factory to use when building the associated instance.
|
||||
# If no name is given, the name of the attribute is assumed to be the
|
||||
# name of the factory. For example, a "user" association will by
|
||||
# default use the "user" factory.
|
||||
def association(name, options = {})
|
||||
factory_name = options.delete(:factory) || name
|
||||
@factory.define_attribute(Attribute::Association.new(name, factory_name, options))
|
||||
end
|
||||
|
||||
def after_build(&block)
|
||||
@factory.add_callback(:after_build, &block)
|
||||
end
|
||||
|
||||
def after_create(&block)
|
||||
@factory.add_callback(:after_create, &block)
|
||||
end
|
||||
|
||||
def after_stub(&block)
|
||||
@factory.add_callback(:after_stub, &block)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,7 +1,4 @@
|
|||
class Factory
|
||||
undef :id if Factory.instance_methods.include?('id')
|
||||
undef :type if Factory.instance_methods.include?('type')
|
||||
|
||||
# Raised when a factory is defined that attempts to instantiate itself.
|
||||
class AssociationDefinitionError < RuntimeError
|
||||
end
|
||||
|
@ -30,37 +27,12 @@ class Factory
|
|||
attr_reader :factory_name #:nodoc:
|
||||
attr_reader :attributes #:nodoc:
|
||||
|
||||
# Defines a new factory that can be used by the build strategies (create and
|
||||
# build) to build new objects.
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+ or +String+
|
||||
# A unique name used to identify this factory.
|
||||
# * options: +Hash+
|
||||
#
|
||||
# Options:
|
||||
# * class: +Symbol+, +Class+, or +String+
|
||||
# The class that will be used when generating instances for this factory. If not specified, the class will be guessed from the factory name.
|
||||
# * parent: +Symbol+
|
||||
# The parent factory. If specified, the attributes from the parent
|
||||
# factory will be copied to the current one with an ability to override
|
||||
# them.
|
||||
# * default_strategy: +Symbol+
|
||||
# The strategy that will be used by the Factory shortcut method.
|
||||
# Defaults to :create.
|
||||
#
|
||||
# Yields: +Factory+
|
||||
# The newly created factory.
|
||||
def self.define (name, options = {})
|
||||
instance = Factory.new(name, options)
|
||||
yield(instance)
|
||||
if parent = options.delete(:parent)
|
||||
instance.inherit_from(Factory.factory_by_name(parent))
|
||||
end
|
||||
if self.factories[instance.factory_name]
|
||||
def self.register_factory(factory)
|
||||
name = factory.factory_name
|
||||
if self.factories[name]
|
||||
raise DuplicateDefinitionError, "Factory already defined: #{name}"
|
||||
end
|
||||
self.factories[instance.factory_name] = instance
|
||||
self.factories[name] = factory
|
||||
end
|
||||
|
||||
def class_name #:nodoc:
|
||||
|
@ -92,215 +64,25 @@ class Factory
|
|||
end
|
||||
end
|
||||
|
||||
# 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 overridden for a specific instance.
|
||||
#
|
||||
# When defining lazy attributes, an instance of Factory::Proxy will
|
||||
# be yielded, allowing associations to be built using the correct build
|
||||
# strategy.
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+ or +String+
|
||||
# The name of this attribute. This will be assigned using :"#{name}=" for
|
||||
# generated instances.
|
||||
# * value: +Object+
|
||||
# If no block is given, this value will be used for this attribute.
|
||||
def add_attribute (name, value = nil, &block)
|
||||
if block_given?
|
||||
if value
|
||||
raise AttributeDefinitionError, "Both value and block given"
|
||||
else
|
||||
attribute = Attribute::Dynamic.new(name, block)
|
||||
end
|
||||
else
|
||||
attribute = Attribute::Static.new(name, value)
|
||||
end
|
||||
|
||||
if attribute_defined?(attribute.name)
|
||||
def define_attribute(attribute)
|
||||
name = attribute.name
|
||||
# TODO: move these checks into Attribute
|
||||
if attribute_defined?(name)
|
||||
raise AttributeDefinitionError, "Attribute already defined: #{name}"
|
||||
end
|
||||
|
||||
if attribute.respond_to?(:factory) && attribute.factory == self.factory_name
|
||||
raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.factory_name}'"
|
||||
end
|
||||
@attributes << attribute
|
||||
end
|
||||
|
||||
# Calls add_attribute using the missing method name as the name of the
|
||||
# attribute, so that:
|
||||
#
|
||||
# Factory.define :user do |f|
|
||||
# f.name 'Billy Idol'
|
||||
# end
|
||||
#
|
||||
# and:
|
||||
#
|
||||
# Factory.define :user do |f|
|
||||
# f.add_attribute :name, 'Billy Idol'
|
||||
# end
|
||||
#
|
||||
# are equivilent.
|
||||
def method_missing (name, *args, &block)
|
||||
add_attribute(name, *args, &block)
|
||||
end
|
||||
|
||||
# Adds an attribute that builds an association. The associated instance will
|
||||
# be built using the same build strategy as the parent instance.
|
||||
#
|
||||
# Example:
|
||||
# Factory.define :user do |f|
|
||||
# f.name 'Joey'
|
||||
# end
|
||||
#
|
||||
# Factory.define :post do |f|
|
||||
# f.association :author, :factory => :user
|
||||
# end
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+
|
||||
# The name of this attribute.
|
||||
# * options: +Hash+
|
||||
#
|
||||
# Options:
|
||||
# * factory: +Symbol+ or +String+
|
||||
# The name of the factory to use when building the associated instance.
|
||||
# If no name is given, the name of the attribute is assumed to be the
|
||||
# name of the factory. For example, a "user" association will by
|
||||
# default use the "user" factory.
|
||||
def association (name, options = {})
|
||||
factory_name = options.delete(:factory) || name
|
||||
if factory_name_for(factory_name) == self.factory_name
|
||||
raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.factory_name}'"
|
||||
end
|
||||
@attributes << Attribute::Association.new(name, factory_name, options)
|
||||
end
|
||||
|
||||
# Adds an attribute that will have unique values generated by a sequence with
|
||||
# a specified format.
|
||||
#
|
||||
# The result of:
|
||||
# Factory.define :user do |f|
|
||||
# f.sequence(:email) { |n| "person#{n}@example.com" }
|
||||
# end
|
||||
#
|
||||
# Is equal to:
|
||||
# Factory.sequence(:email) { |n| "person#{n}@example.com" }
|
||||
#
|
||||
# Factory.define :user do |f|
|
||||
# f.email { Factory.next(:email) }
|
||||
# end
|
||||
#
|
||||
# Except that no globally available sequence will be defined.
|
||||
def sequence (name, &block)
|
||||
s = Sequence.new(&block)
|
||||
add_attribute(name) { s.next }
|
||||
end
|
||||
|
||||
def after_build(&block)
|
||||
callback(:after_build, &block)
|
||||
end
|
||||
|
||||
def after_create(&block)
|
||||
callback(:after_create, &block)
|
||||
end
|
||||
|
||||
def after_stub(&block)
|
||||
callback(:after_stub, &block)
|
||||
end
|
||||
|
||||
def callback(name, &block)
|
||||
def add_callback(name, &block)
|
||||
unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
|
||||
raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
|
||||
end
|
||||
@attributes << Attribute::Callback.new(name.to_sym, block)
|
||||
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 = {})
|
||||
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 = {})
|
||||
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 = {})
|
||||
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 = {})
|
||||
factory_by_name(name).run(Proxy::Stub, overrides)
|
||||
end
|
||||
|
||||
# Executes the default strategy for the given factory. This is usually create,
|
||||
# but it can be overridden for each factory.
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+ or +String+
|
||||
# The name of the factory that should be used.
|
||||
# * overrides: +Hash+
|
||||
# Attributes to overwrite for this instance.
|
||||
#
|
||||
# Returns: +Object+
|
||||
# The result of the default strategy.
|
||||
def self.default_strategy (name, overrides = {})
|
||||
self.send(factory_by_name(name).default_strategy, name, overrides)
|
||||
end
|
||||
|
||||
def self.find_definitions #:nodoc:
|
||||
definition_file_paths.each do |path|
|
||||
require("#{path}.rb") if File.exists?("#{path}.rb")
|
||||
|
|
|
@ -27,8 +27,9 @@ class Factory
|
|||
|
||||
def blueprint(&block)
|
||||
instance = Factory.new(name.underscore, :class => self)
|
||||
instance.instance_eval(&block)
|
||||
Factory.factories[instance.factory_name] = instance
|
||||
proxy = Factory::DefinitionProxy.new(instance)
|
||||
proxy.instance_eval(&block)
|
||||
Factory.register_factory(instance)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
118
lib/factory_girl/syntax/default.rb
Normal file
118
lib/factory_girl/syntax/default.rb
Normal file
|
@ -0,0 +1,118 @@
|
|||
class Factory
|
||||
|
||||
# Defines a new factory that can be used by the build strategies (create and
|
||||
# build) to build new objects.
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+ or +String+
|
||||
# A unique name used to identify this factory.
|
||||
# * options: +Hash+
|
||||
#
|
||||
# Options:
|
||||
# * class: +Symbol+, +Class+, or +String+
|
||||
# The class that will be used when generating instances for this factory. If not specified, the class will be guessed from the factory name.
|
||||
# * parent: +Symbol+
|
||||
# The parent factory. If specified, the attributes from the parent
|
||||
# factory will be copied to the current one with an ability to override
|
||||
# them.
|
||||
# * default_strategy: +Symbol+
|
||||
# The strategy that will be used by the Factory shortcut method.
|
||||
# Defaults to :create.
|
||||
#
|
||||
# Yields: +Factory+
|
||||
# The newly created factory.
|
||||
def self.define(name, options = {})
|
||||
factory = Factory.new(name, options)
|
||||
proxy = Factory::DefinitionProxy.new(factory)
|
||||
yield(proxy)
|
||||
if parent = options.delete(:parent)
|
||||
factory.inherit_from(Factory.factory_by_name(parent))
|
||||
end
|
||||
register_factory(factory)
|
||||
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 = {})
|
||||
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 = {})
|
||||
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 = {})
|
||||
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 = {})
|
||||
factory_by_name(name).run(Proxy::Stub, overrides)
|
||||
end
|
||||
|
||||
# Executes the default strategy for the given factory. This is usually create,
|
||||
# but it can be overridden for each factory.
|
||||
#
|
||||
# Arguments:
|
||||
# * name: +Symbol+ or +String+
|
||||
# The name of the factory that should be used.
|
||||
# * overrides: +Hash+
|
||||
# Attributes to overwrite for this instance.
|
||||
#
|
||||
# Returns: +Object+
|
||||
# The result of the default strategy.
|
||||
def self.default_strategy (name, overrides = {})
|
||||
self.send(factory_by_name(name).default_strategy, name, overrides)
|
||||
end
|
||||
|
||||
end
|
|
@ -39,7 +39,7 @@ describe "integration" do
|
|||
end
|
||||
|
||||
Factory.define :user_with_inherited_callbacks, :parent => :user_with_callbacks do |f|
|
||||
f.callback(:after_stub) {|u| u.last_name = 'Double-Stubby' }
|
||||
f.after_stub {|u| u.last_name = 'Double-Stubby' }
|
||||
end
|
||||
|
||||
Factory.define :business do |f|
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'spec_helper'
|
||||
require 'acceptance/acceptance_helper'
|
||||
|
||||
require 'factory_girl/syntax/blueprint'
|
||||
|
||||
|
@ -12,11 +13,6 @@ describe "a blueprint" do
|
|||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Factory.factories.clear
|
||||
Factory.sequences.clear
|
||||
end
|
||||
|
||||
describe "after making an instance" do
|
||||
before do
|
||||
@instance = Factory(:user, :last_name => 'Rye')
|
||||
|
|
143
spec/acceptance/syntax/default_spec.rb
Normal file
143
spec/acceptance/syntax/default_spec.rb
Normal file
|
@ -0,0 +1,143 @@
|
|||
require 'spec_helper'
|
||||
require 'acceptance/acceptance_helper'
|
||||
|
||||
describe "default syntax" do
|
||||
before do
|
||||
Factory.sequence(:email) { |n| "somebody#{n}@example.com" }
|
||||
Factory.define :user do |factory|
|
||||
factory.first_name { 'Bill' }
|
||||
factory.last_name { 'Nye' }
|
||||
factory.email { Factory.next(:email) }
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Factory.factories.clear
|
||||
Factory.sequences.clear
|
||||
end
|
||||
|
||||
describe "after making an instance" do
|
||||
before do
|
||||
@instance = Factory(:user, :last_name => 'Rye')
|
||||
end
|
||||
|
||||
it "should use attributes from the definition" do
|
||||
@instance.first_name.should == 'Bill'
|
||||
end
|
||||
|
||||
it "should evaluate attribute blocks for each instance" do
|
||||
@instance.email.should =~ /somebody\d+@example.com/
|
||||
Factory(:user).email.should_not == @instance.email
|
||||
end
|
||||
end
|
||||
|
||||
it "should raise an ArgumentError when trying to use a non-existent strategy" do
|
||||
lambda {
|
||||
Factory.define(:object, :default_strategy => :nonexistent) {}
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "given a parent factory" do
|
||||
before do
|
||||
@parent = Factory.new(:object)
|
||||
@parent.define_attribute(Factory::Attribute::Static.new(:name, 'value'))
|
||||
Factory.register_factory(@parent)
|
||||
end
|
||||
|
||||
it "should raise an ArgumentError when trying to use a non-existent factory as parent" do
|
||||
lambda {
|
||||
Factory.define(:child, :parent => :nonexsitent) {}
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "defining a factory" do
|
||||
before do
|
||||
@name = :user
|
||||
@factory = "factory"
|
||||
@proxy = "proxy"
|
||||
stub(@factory).factory_name { @name }
|
||||
@options = { :class => 'magic' }
|
||||
stub(Factory).new { @factory }
|
||||
stub(Factory::DefinitionProxy).new { @proxy }
|
||||
end
|
||||
|
||||
after { Factory.factories.clear }
|
||||
|
||||
it "should create a new factory using the specified name and options" do
|
||||
mock(Factory).new(@name, @options) { @factory }
|
||||
Factory.define(@name, @options) {|f| }
|
||||
end
|
||||
|
||||
it "should pass the factory do the block" do
|
||||
yielded = nil
|
||||
Factory.define(@name) do |y|
|
||||
yielded = y
|
||||
end
|
||||
yielded.should == @proxy
|
||||
end
|
||||
|
||||
it "should add the factory to the list of factories" do
|
||||
Factory.define(@name) {|f| }
|
||||
@factory.should == Factory.factories[@name]
|
||||
end
|
||||
|
||||
it "should allow a factory to be found by name" do
|
||||
Factory.define(@name) {|f| }
|
||||
Factory.factory_by_name(@name).should == @factory
|
||||
end
|
||||
end
|
||||
|
||||
describe "after defining a factory" do
|
||||
before do
|
||||
@name = :user
|
||||
@factory = "factory"
|
||||
|
||||
Factory.factories[@name] = @factory
|
||||
end
|
||||
|
||||
it "should use Proxy::AttributesFor for Factory.attributes_for" do
|
||||
mock(@factory).run(Factory::Proxy::AttributesFor, :attr => 'value') { 'result' }
|
||||
Factory.attributes_for(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use Proxy::Build for Factory.build" do
|
||||
mock(@factory).run(Factory::Proxy::Build, :attr => 'value') { 'result' }
|
||||
Factory.build(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use Proxy::Create for Factory.create" do
|
||||
mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
|
||||
Factory.create(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use Proxy::Stub for Factory.stub" do
|
||||
mock(@factory).run(Factory::Proxy::Stub, :attr => 'value') { 'result' }
|
||||
Factory.stub(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use default strategy option as Factory.default_strategy" do
|
||||
stub(@factory).default_strategy { :create }
|
||||
mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
|
||||
Factory.default_strategy(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use the default strategy for the global Factory method" do
|
||||
stub(@factory).default_strategy { :create }
|
||||
mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
|
||||
Factory(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
[:build, :create, :attributes_for, :stub].each do |method|
|
||||
it "should raise an ArgumentError on #{method} with a nonexistant factory" do
|
||||
lambda { Factory.send(method, :bogus) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should recognize either 'name' or :name for Factory.#{method}" do
|
||||
stub(@factory).run
|
||||
lambda { Factory.send(method, @name.to_s) }.should_not raise_error
|
||||
lambda { Factory.send(method, @name.to_sym) }.should_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,5 @@
|
|||
require 'spec_helper'
|
||||
require 'acceptance/acceptance_helper'
|
||||
|
||||
require 'factory_girl/syntax/generate'
|
||||
|
||||
|
@ -11,10 +12,6 @@ describe "a factory using generate syntax" do
|
|||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Factory.factories.clear
|
||||
end
|
||||
|
||||
it "should not raise an error when generating an invalid instance" do
|
||||
lambda { User.generate(:first_name => nil) }.should_not raise_error
|
||||
end
|
||||
|
|
100
spec/factory_girl/definition_proxy_spec.rb
Normal file
100
spec/factory_girl/definition_proxy_spec.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Factory::DefinitionProxy do
|
||||
let(:factory) { Factory.new(:object) }
|
||||
subject { Factory::DefinitionProxy.new(factory) }
|
||||
|
||||
it "should add a static attribute for type" do
|
||||
subject.type
|
||||
factory.attributes.last.should be_kind_of(Factory::Attribute::Static)
|
||||
end
|
||||
|
||||
it "should add a static attribute for id" do
|
||||
subject.id
|
||||
factory.attributes.last.should be_kind_of(Factory::Attribute::Static)
|
||||
end
|
||||
|
||||
it "should add a static attribute when an attribute is defined with a value" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
mock(Factory::Attribute::Static).new(:name, 'value') { attribute }
|
||||
mock(factory).define_attribute(attribute)
|
||||
subject.add_attribute(:name, 'value')
|
||||
end
|
||||
|
||||
it "should add a dynamic attribute when an attribute is defined with a block" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
block = lambda {}
|
||||
mock(Factory::Attribute::Dynamic).new(:name, block) { attribute }
|
||||
mock(factory).define_attribute(attribute)
|
||||
subject.add_attribute(:name, &block)
|
||||
end
|
||||
|
||||
it "should raise for an attribute with a value and a block" do
|
||||
lambda {
|
||||
subject.add_attribute(:name, 'value') {}
|
||||
}.should raise_error(Factory::AttributeDefinitionError)
|
||||
end
|
||||
|
||||
describe "adding an attribute using a in-line sequence" do
|
||||
it "should create the sequence" do
|
||||
mock(Factory::Sequence).new
|
||||
subject.sequence(:name) {}
|
||||
end
|
||||
|
||||
it "should add a dynamic attribute" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
mock(Factory::Attribute::Dynamic).new(:name, is_a(Proc)) { attribute }
|
||||
subject.sequence(:name) {}
|
||||
factory.attributes.should include(attribute)
|
||||
end
|
||||
end
|
||||
|
||||
it "should add a callback attribute when the after_build attribute is defined" do
|
||||
mock(Factory::Attribute::Callback).new(:after_build, is_a(Proc)) { 'after_build callback' }
|
||||
subject.after_build {}
|
||||
factory.attributes.should include('after_build callback')
|
||||
end
|
||||
|
||||
it "should add a callback attribute when the after_create attribute is defined" do
|
||||
mock(Factory::Attribute::Callback).new(:after_create, is_a(Proc)) { 'after_create callback' }
|
||||
subject.after_create {}
|
||||
factory.attributes.should include('after_create callback')
|
||||
end
|
||||
|
||||
it "should add a callback attribute when the after_stub attribute is defined" do
|
||||
mock(Factory::Attribute::Callback).new(:after_stub, is_a(Proc)) { 'after_stub callback' }
|
||||
subject.after_stub {}
|
||||
factory.attributes.should include('after_stub callback')
|
||||
end
|
||||
|
||||
it "should add an association without a factory name or overrides" do
|
||||
name = :user
|
||||
attr = 'attribute'
|
||||
stub(attr).name { name }
|
||||
mock(Factory::Attribute::Association).new(name, name, {}) { attr }
|
||||
subject.association(name)
|
||||
factory.attributes.should include(attr)
|
||||
end
|
||||
|
||||
it "should add an association with overrides" do
|
||||
name = :user
|
||||
attr = 'attribute'
|
||||
overrides = { :first_name => 'Ben' }
|
||||
stub(attr).name { name }
|
||||
mock(Factory::Attribute::Association).new(name, name, overrides) { attr }
|
||||
subject.association(name, overrides)
|
||||
factory.attributes.should include(attr)
|
||||
end
|
||||
|
||||
it "should add an attribute using the method name when passed an undefined method" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
block = lambda {}
|
||||
mock(Factory::Attribute::Static).new(:name, 'value') { attribute }
|
||||
subject.send(:name, 'value')
|
||||
factory.attributes.should include(attribute)
|
||||
end
|
||||
end
|
|
@ -1,581 +1,361 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Factory, "registering a factory" do
|
||||
before do
|
||||
@name = :user
|
||||
@factory = "factory"
|
||||
stub(@factory).factory_name { @name }
|
||||
end
|
||||
|
||||
after { Factory.factories.clear }
|
||||
|
||||
it "should add the factory to the list of factories" do
|
||||
Factory.register_factory(@factory)
|
||||
Factory.factory_by_name(@name).should == @factory
|
||||
end
|
||||
|
||||
it "should not allow a duplicate factory definition" do
|
||||
lambda {
|
||||
2.times { Factory.register_factory(@factory) }
|
||||
}.should raise_error(Factory::DuplicateDefinitionError)
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory do
|
||||
include DefinesConstants
|
||||
|
||||
before do
|
||||
@name = :user
|
||||
@class = define_constant('User')
|
||||
@factory = Factory.new(@name)
|
||||
end
|
||||
|
||||
it "should have a factory name" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
|
||||
it "should have a build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
|
||||
it "should have a default strategy" do
|
||||
@factory.default_strategy.should == :create
|
||||
end
|
||||
|
||||
it "should not allow the same attribute to be added twice" do
|
||||
lambda {
|
||||
2.times { @factory.define_attribute Factory::Attribute::Static.new(:name, 'value') }
|
||||
}.should raise_error(Factory::AttributeDefinitionError)
|
||||
end
|
||||
|
||||
it "should add a callback attribute when defining a callback" do
|
||||
mock(Factory::Attribute::Callback).new(:after_create, is_a(Proc)) { 'after_create callback' }
|
||||
@factory.add_callback(:after_create) {}
|
||||
@factory.attributes.should include('after_create callback')
|
||||
end
|
||||
|
||||
it "should raise an InvalidCallbackNameError when defining a callback with an invalid name" do
|
||||
lambda{
|
||||
@factory.add_callback(:invalid_callback_name) {}
|
||||
}.should raise_error(Factory::InvalidCallbackNameError)
|
||||
end
|
||||
|
||||
describe "after adding an attribute" do
|
||||
before do
|
||||
@attribute = "attribute"
|
||||
@proxy = "proxy"
|
||||
|
||||
stub(@attribute).name { :name }
|
||||
stub(@attribute).add_to
|
||||
stub(@proxy).set
|
||||
stub(@proxy).result { 'result' }
|
||||
stub(Factory::Attribute::Static).new { @attribute }
|
||||
stub(Factory::Proxy::Build).new { @proxy }
|
||||
|
||||
@factory.define_attribute(@attribute)
|
||||
end
|
||||
|
||||
it "should create the right proxy using the build class when running" do
|
||||
mock(Factory::Proxy::Build).new(@factory.build_class) { @proxy }
|
||||
@factory.run(Factory::Proxy::Build, {})
|
||||
end
|
||||
|
||||
it "should add the attribute to the proxy when running" do
|
||||
mock(@attribute).add_to(@proxy)
|
||||
@factory.run(Factory::Proxy::Build, {})
|
||||
end
|
||||
|
||||
it "should return the result from the proxy when running" do
|
||||
mock(@proxy).result() { 'result' }
|
||||
@factory.run(Factory::Proxy::Build, {}).should == 'result'
|
||||
end
|
||||
end
|
||||
|
||||
it "should return associations" do
|
||||
factory = Factory.new(:post)
|
||||
factory.define_attribute(Factory::Attribute::Association.new(:author, :author, {}))
|
||||
factory.define_attribute(Factory::Attribute::Association.new(:editor, :editor, {}))
|
||||
factory.associations.each do |association|
|
||||
association.should be_a(Factory::Attribute::Association)
|
||||
end
|
||||
factory.associations.size.should == 2
|
||||
end
|
||||
|
||||
it "should raise for a self referencing association" do
|
||||
factory = Factory.new(:post)
|
||||
lambda {
|
||||
factory.define_attribute(Factory::Attribute::Association.new(:parent, :post, {}))
|
||||
}.should raise_error(Factory::AssociationDefinitionError)
|
||||
end
|
||||
|
||||
describe "when overriding generated attributes with a hash" do
|
||||
before do
|
||||
@name = :name
|
||||
@value = 'The price is right!'
|
||||
@hash = { @name => @value }
|
||||
end
|
||||
|
||||
it "should return the overridden value in the generated attributes" do
|
||||
attr = Factory::Attribute::Static.new(@name, 'The price is wrong, Bob!')
|
||||
@factory.define_attribute(attr)
|
||||
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
||||
result[@name].should == @value
|
||||
end
|
||||
|
||||
it "should not call a lazy attribute block for an overridden attribute" do
|
||||
attr = Factory::Attribute::Dynamic.new(@name, lambda { flunk })
|
||||
@factory.define_attribute(attr)
|
||||
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
||||
end
|
||||
|
||||
it "should override a symbol parameter with a string parameter" do
|
||||
attr = Factory::Attribute::Static.new(@name, 'The price is wrong, Bob!')
|
||||
@factory.define_attribute(attr)
|
||||
@hash = { @name.to_s => @value }
|
||||
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
||||
result[@name].should == @value
|
||||
end
|
||||
end
|
||||
|
||||
describe "overriding an attribute with an alias" do
|
||||
before do
|
||||
@factory.define_attribute(Factory::Attribute::Static.new(:test, 'original'))
|
||||
Factory.alias(/(.*)_alias/, '\1')
|
||||
@result = @factory.run(Factory::Proxy::AttributesFor,
|
||||
:test_alias => 'new')
|
||||
end
|
||||
|
||||
it "should use the passed in value for the alias" do
|
||||
@result[:test_alias].should == 'new'
|
||||
end
|
||||
|
||||
it "should discard the predefined value for the attribute" do
|
||||
@result[:test].should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "should guess the build class from the factory name" do
|
||||
@factory.build_class.should == User
|
||||
end
|
||||
|
||||
it "should create a new factory using the class of the parent" do
|
||||
child = Factory.new(:child)
|
||||
child.inherit_from(@factory)
|
||||
child.build_class.should == @factory.build_class
|
||||
end
|
||||
|
||||
it "should create a new factory while overriding the parent class" do
|
||||
child = Factory.new(:child, :class => String)
|
||||
child.inherit_from(@factory)
|
||||
child.build_class.should == String
|
||||
end
|
||||
|
||||
describe "given a parent with attributes" do
|
||||
before do
|
||||
@parent_attr = :name
|
||||
@factory.define_attribute(Factory::Attribute::Static.new(@parent_attr, 'value'))
|
||||
end
|
||||
|
||||
it "should create a new factory with attributes of the parent" do
|
||||
child = Factory.new(:child)
|
||||
child.inherit_from(@factory)
|
||||
child.attributes.size.should == 1
|
||||
child.attributes.first.name.should == @parent_attr
|
||||
end
|
||||
|
||||
it "should allow a child to define additional attributes" do
|
||||
child = Factory.new(:child)
|
||||
child.define_attribute(Factory::Attribute::Static.new(:email, 'value'))
|
||||
child.inherit_from(@factory)
|
||||
child.attributes.size.should == 2
|
||||
end
|
||||
|
||||
it "should allow to override parent attributes" do
|
||||
child = Factory.new(:child)
|
||||
@child_attr = Factory::Attribute::Static.new(@parent_attr, 'value')
|
||||
child.define_attribute(@child_attr)
|
||||
child.inherit_from(@factory)
|
||||
child.attributes.size.should == 1
|
||||
child.attributes.first.should == @child_attr
|
||||
end
|
||||
end
|
||||
|
||||
it "inherit all callbacks" do
|
||||
@factory.add_callback(:after_stub) { |object| object.name = 'Stubby' }
|
||||
child = Factory.new(:child)
|
||||
child.inherit_from(@factory)
|
||||
child.attributes.last.should be_kind_of(Factory::Attribute::Callback)
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "when defined with a custom class" do
|
||||
before do
|
||||
@class = Float
|
||||
@factory = Factory.new(:author, :class => @class)
|
||||
end
|
||||
|
||||
it "should use the specified class as the build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "when defined with a class instead of a name" do
|
||||
before do
|
||||
@class = ArgumentError
|
||||
@name = :argument_error
|
||||
@factory = Factory.new(@class)
|
||||
end
|
||||
|
||||
it "should guess the name from the class" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
|
||||
it "should use the class as the build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "when defined with a custom class name" do
|
||||
before do
|
||||
@class = ArgumentError
|
||||
@factory = Factory.new(:author, :class => :argument_error)
|
||||
end
|
||||
|
||||
it "should use the specified class as the build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "with a name ending in s" do
|
||||
include DefinesConstants
|
||||
|
||||
before do
|
||||
define_constant('Business')
|
||||
@name = :business
|
||||
@class = Business
|
||||
@factory = Factory.new(@name)
|
||||
end
|
||||
|
||||
it "should have a factory name" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
|
||||
it "should have a build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "with a string for a name" do
|
||||
before do
|
||||
@name = :string
|
||||
@factory = Factory.new(@name.to_s) {}
|
||||
end
|
||||
|
||||
it "should convert the string to a symbol" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "registered with a string name" do
|
||||
before do
|
||||
@name = :string
|
||||
@factory = Factory.new(@name)
|
||||
Factory.register_factory(@factory)
|
||||
end
|
||||
|
||||
it "should store the factory using a symbol" do
|
||||
Factory.factories[@name].should == @factory
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory, "for namespaced class" do
|
||||
include DefinesConstants
|
||||
|
||||
before do
|
||||
define_constant('Admin')
|
||||
define_constant('Admin::Settings')
|
||||
|
||||
@name = :settings
|
||||
@class = Admin::Settings
|
||||
end
|
||||
|
||||
it "should build namespaced class passed by string" do
|
||||
factory = Factory.new(@name.to_s, :class => @class.name)
|
||||
factory.build_class.should == @class
|
||||
end
|
||||
|
||||
it "should build Admin::Settings class from Admin::Settings string" do
|
||||
factory = Factory.new(@name.to_s, :class => 'admin/settings')
|
||||
factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe Factory do
|
||||
include DefinesConstants
|
||||
|
||||
before do
|
||||
define_constant('User')
|
||||
define_constant('Admin', User)
|
||||
define_constant('Business')
|
||||
define_constant('Admin::Settings')
|
||||
end
|
||||
|
||||
describe "defining a factory" do
|
||||
it "should raise an ArgumentError when trying to use a non-existent strategy" do
|
||||
lambda {
|
||||
Factory.new(:object, :default_strategy => :nonexistent) {}
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should create a new factory with a specified default strategy" do
|
||||
factory = Factory.new(:object, :default_strategy => :stub)
|
||||
factory.default_strategy.should == :stub
|
||||
end
|
||||
|
||||
describe 'defining a child factory without setting default strategy' do
|
||||
before do
|
||||
@name = :user
|
||||
@factory = "factory"
|
||||
stub(@factory).factory_name { @name }
|
||||
@options = { :class => 'magic' }
|
||||
stub(Factory).new { @factory }
|
||||
@parent = Factory.new(:object, :default_strategy => :stub)
|
||||
@child = Factory.new(:child_object)
|
||||
@child.inherit_from(@parent)
|
||||
end
|
||||
|
||||
after { Factory.factories.clear }
|
||||
|
||||
it "should create a new factory using the specified name and options" do
|
||||
mock(Factory).new(@name, @options) { @factory }
|
||||
Factory.define(@name, @options) {|f| }
|
||||
it "should inherit default strategy from its parent" do
|
||||
@child.default_strategy.should == :stub
|
||||
end
|
||||
|
||||
it "should pass the factory do the block" do
|
||||
yielded = nil
|
||||
Factory.define(@name) do |y|
|
||||
yielded = y
|
||||
end
|
||||
yielded.should == @factory
|
||||
end
|
||||
|
||||
it "should add the factory to the list of factories" do
|
||||
Factory.define(@name) {|f| }
|
||||
@factory.should == Factory.factories[@name]
|
||||
end
|
||||
|
||||
it "should allow a factory to be found by name" do
|
||||
Factory.define(@name) {|f| }
|
||||
Factory.factory_by_name(@name).should == @factory
|
||||
end
|
||||
|
||||
it "should not allow a duplicate factory definition" do
|
||||
lambda {
|
||||
2.times { Factory.define(@name) {|f| } }
|
||||
}.should raise_error(Factory::DuplicateDefinitionError)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "a factory" do
|
||||
describe 'defining a child factory with a default strategy' do
|
||||
before do
|
||||
@name = :user
|
||||
@class = User
|
||||
@factory = Factory.new(@name)
|
||||
@parent = Factory.new(:object, :default_strategy => :stub)
|
||||
@child = Factory.new(:child_object2, :default_strategy => :build)
|
||||
@child.inherit_from(@parent)
|
||||
end
|
||||
|
||||
it "should have a factory name" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
|
||||
it "should have a build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
|
||||
it "should have a default strategy" do
|
||||
@factory.default_strategy.should == :create
|
||||
end
|
||||
|
||||
it "should return static attribute when asked for a type" do
|
||||
result = @factory.type
|
||||
result.should be_kind_of(Array)
|
||||
result.first.should be_kind_of(Factory::Attribute::Static)
|
||||
result.first.name.should == :type
|
||||
end
|
||||
|
||||
it "should define type as an attribute" do
|
||||
@factory.type { "it's a type" }
|
||||
attributes = @factory.attributes
|
||||
attributes.should be_kind_of(Array)
|
||||
attributes.size.should == 1
|
||||
attributes.first.name.should == :type
|
||||
end
|
||||
|
||||
it "should return static attribute when asked for the id" do
|
||||
result = @factory.id
|
||||
result.should be_kind_of(Array)
|
||||
result.first.should be_kind_of(Factory::Attribute::Static)
|
||||
result.first.name.should == :id
|
||||
end
|
||||
|
||||
it "should define id as an attribute" do
|
||||
@factory.id { "it's an id" }
|
||||
attributes = @factory.attributes
|
||||
attributes.should be_kind_of(Array)
|
||||
attributes.size.should == 1
|
||||
attributes.first.name.should == :id
|
||||
end
|
||||
|
||||
it "should not allow the same attribute to be added twice" do
|
||||
lambda {
|
||||
2.times { @factory.add_attribute :first_name }
|
||||
}.should raise_error(Factory::AttributeDefinitionError)
|
||||
end
|
||||
|
||||
it "should add a static attribute when an attribute is defined with a value" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
mock(Factory::Attribute::Static).new(:name, 'value') { attribute }
|
||||
@factory.add_attribute(:name, 'value')
|
||||
end
|
||||
|
||||
it "should add a dynamic attribute when an attribute is defined with a block" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
block = lambda {}
|
||||
mock(Factory::Attribute::Dynamic).new(:name, block) { attribute }
|
||||
@factory.add_attribute(:name, &block)
|
||||
end
|
||||
|
||||
it "should raise for an attribute with a value and a block" do
|
||||
lambda {
|
||||
@factory.add_attribute(:name, 'value') {}
|
||||
}.should raise_error(Factory::AttributeDefinitionError)
|
||||
end
|
||||
|
||||
describe "adding an attribute using a in-line sequence" do
|
||||
it "should create the sequence" do
|
||||
mock(Factory::Sequence).new
|
||||
@factory.sequence(:name) {}
|
||||
end
|
||||
|
||||
it "should add a dynamic attribute" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
mock(Factory::Attribute::Dynamic).new(:name, is_a(Proc)) { attribute }
|
||||
@factory.sequence(:name) {}
|
||||
@factory.attributes.should include(attribute)
|
||||
end
|
||||
end
|
||||
|
||||
describe "adding a callback" do
|
||||
it "should add a callback attribute when the after_build attribute is defined" do
|
||||
mock(Factory::Attribute::Callback).new(:after_build, is_a(Proc)) { 'after_build callback' }
|
||||
@factory.after_build {}
|
||||
@factory.attributes.should include('after_build callback')
|
||||
end
|
||||
|
||||
it "should add a callback attribute when the after_create attribute is defined" do
|
||||
mock(Factory::Attribute::Callback).new(:after_create, is_a(Proc)) { 'after_create callback' }
|
||||
@factory.after_create {}
|
||||
@factory.attributes.should include('after_create callback')
|
||||
end
|
||||
|
||||
it "should add a callback attribute when the after_stub attribute is defined" do
|
||||
mock(Factory::Attribute::Callback).new(:after_stub, is_a(Proc)) { 'after_stub callback' }
|
||||
@factory.after_stub {}
|
||||
@factory.attributes.should include('after_stub callback')
|
||||
end
|
||||
|
||||
it "should add a callback attribute when defining a callback" do
|
||||
mock(Factory::Attribute::Callback).new(:after_create, is_a(Proc)) { 'after_create callback' }
|
||||
@factory.callback(:after_create) {}
|
||||
@factory.attributes.should include('after_create callback')
|
||||
end
|
||||
|
||||
it "should raise an InvalidCallbackNameError when defining a callback with an invalid name" do
|
||||
lambda{
|
||||
@factory.callback(:invalid_callback_name) {}
|
||||
}.should raise_error(Factory::InvalidCallbackNameError)
|
||||
end
|
||||
end
|
||||
|
||||
describe "after adding an attribute" do
|
||||
before do
|
||||
@attribute = "attribute"
|
||||
@proxy = "proxy"
|
||||
|
||||
stub(@attribute).name { :name }
|
||||
stub(@attribute).add_to
|
||||
stub(@proxy).set
|
||||
stub(@proxy).result { 'result' }
|
||||
stub(Factory::Attribute::Static).new { @attribute }
|
||||
stub(Factory::Proxy::Build).new { @proxy }
|
||||
|
||||
@factory.add_attribute(:name, 'value')
|
||||
end
|
||||
|
||||
it "should create the right proxy using the build class when running" do
|
||||
mock(Factory::Proxy::Build).new(@factory.build_class) { @proxy }
|
||||
@factory.run(Factory::Proxy::Build, {})
|
||||
end
|
||||
|
||||
it "should add the attribute to the proxy when running" do
|
||||
mock(@attribute).add_to(@proxy)
|
||||
@factory.run(Factory::Proxy::Build, {})
|
||||
end
|
||||
|
||||
it "should return the result from the proxy when running" do
|
||||
mock(@proxy).result() { 'result' }
|
||||
@factory.run(Factory::Proxy::Build, {}).should == 'result'
|
||||
end
|
||||
end
|
||||
|
||||
it "should add an association without a factory name or overrides" do
|
||||
factory = Factory.new(:post)
|
||||
name = :user
|
||||
attr = 'attribute'
|
||||
mock(Factory::Attribute::Association).new(name, name, {}) { attr }
|
||||
factory.association(name)
|
||||
factory.attributes.should include(attr)
|
||||
end
|
||||
|
||||
it "should return associations" do
|
||||
factory = Factory.new(:post)
|
||||
factory.association(:author)
|
||||
factory.association(:editor)
|
||||
factory.associations.each do |association|
|
||||
association.should be_a(Factory::Attribute::Association)
|
||||
end
|
||||
factory.associations.size.should == 2
|
||||
end
|
||||
|
||||
it "should add an association with overrides" do
|
||||
factory = Factory.new(:post)
|
||||
name = :user
|
||||
attr = 'attribute'
|
||||
overrides = { :first_name => 'Ben' }
|
||||
mock(Factory::Attribute::Association).new(name, name, overrides) { attr }
|
||||
factory.association(name, overrides)
|
||||
factory.attributes.should include(attr)
|
||||
end
|
||||
|
||||
it "should add an association with a factory name" do
|
||||
factory = Factory.new(:post)
|
||||
attr = 'attribute'
|
||||
mock(Factory::Attribute::Association).new(:author, :user, {}) { attr }
|
||||
factory.association(:author, :factory => :user)
|
||||
factory.attributes.should include(attr)
|
||||
end
|
||||
|
||||
it "should add an association with a factory name and overrides" do
|
||||
factory = Factory.new(:post)
|
||||
attr = 'attribute'
|
||||
mock(Factory::Attribute::Association).new(:author, :user, :first_name => 'Ben') { attr }
|
||||
factory.association(:author, :factory => :user, :first_name => 'Ben')
|
||||
factory.attributes.should include(attr)
|
||||
end
|
||||
|
||||
it "should raise for a self referencing association" do
|
||||
factory = Factory.new(:post)
|
||||
lambda {
|
||||
factory.association(:parent, :factory => :post)
|
||||
}.should raise_error(Factory::AssociationDefinitionError)
|
||||
end
|
||||
|
||||
it "should add an attribute using the method name when passed an undefined method" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
block = lambda {}
|
||||
mock(Factory::Attribute::Static).new(:name, 'value') { attribute }
|
||||
@factory.send(:name, 'value')
|
||||
@factory.attributes.should include(attribute)
|
||||
end
|
||||
|
||||
it "should allow human_name as a static attribute name" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
mock(Factory::Attribute::Static).new(:human_name, 'value') { attribute}
|
||||
@factory.human_name 'value'
|
||||
end
|
||||
|
||||
it "should allow human_name as a dynamic attribute name" do
|
||||
attribute = 'attribute'
|
||||
stub(attribute).name { :name }
|
||||
block = lambda {}
|
||||
mock(Factory::Attribute::Dynamic).new(:human_name, block) { attribute }
|
||||
@factory.human_name(&block)
|
||||
end
|
||||
|
||||
describe "when overriding generated attributes with a hash" do
|
||||
before do
|
||||
@attr = :name
|
||||
@value = 'The price is right!'
|
||||
@hash = { @attr => @value }
|
||||
end
|
||||
|
||||
it "should return the overridden value in the generated attributes" do
|
||||
@factory.add_attribute(@attr, 'The price is wrong, Bob!')
|
||||
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
||||
result[@attr].should == @value
|
||||
end
|
||||
|
||||
it "should not call a lazy attribute block for an overridden attribute" do
|
||||
@factory.add_attribute(@attr) { flunk }
|
||||
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
||||
end
|
||||
|
||||
it "should override a symbol parameter with a string parameter" do
|
||||
@factory.add_attribute(@attr, 'The price is wrong, Bob!')
|
||||
@hash = { @attr.to_s => @value }
|
||||
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
||||
result[@attr].should == @value
|
||||
end
|
||||
end
|
||||
|
||||
describe "overriding an attribute with an alias" do
|
||||
before do
|
||||
@factory.add_attribute(:test, 'original')
|
||||
Factory.alias(/(.*)_alias/, '\1')
|
||||
@result = @factory.run(Factory::Proxy::AttributesFor,
|
||||
:test_alias => 'new')
|
||||
end
|
||||
|
||||
it "should use the passed in value for the alias" do
|
||||
@result[:test_alias].should == 'new'
|
||||
end
|
||||
|
||||
it "should discard the predefined value for the attribute" do
|
||||
@result[:test].should be_nil
|
||||
end
|
||||
end
|
||||
|
||||
it "should guess the build class from the factory name" do
|
||||
@factory.build_class.should == User
|
||||
end
|
||||
|
||||
describe "when defined with a custom class" do
|
||||
before do
|
||||
@class = User
|
||||
@factory = Factory.new(:author, :class => @class)
|
||||
end
|
||||
|
||||
it "should use the specified class as the build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe "when defined with a class instead of a name" do
|
||||
before do
|
||||
@class = ArgumentError
|
||||
@name = :argument_error
|
||||
@factory = Factory.new(@class)
|
||||
end
|
||||
|
||||
it "should guess the name from the class" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
|
||||
it "should use the class as the build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe "when defined with a custom class name" do
|
||||
before do
|
||||
@class = ArgumentError
|
||||
@factory = Factory.new(:author, :class => :argument_error)
|
||||
end
|
||||
|
||||
it "should use the specified class as the build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
it "should override the default strategy from parent" do
|
||||
@child.default_strategy.should == :build
|
||||
end
|
||||
end
|
||||
|
||||
describe "a factory with a name ending in s" do
|
||||
before do
|
||||
@name = :business
|
||||
@class = Business
|
||||
@factory = Factory.new(@name)
|
||||
end
|
||||
|
||||
it "should have a factory name" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
|
||||
it "should have a build class" do
|
||||
@factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe "a factory with a string for a name" do
|
||||
before do
|
||||
@name = :user
|
||||
@factory = Factory.new(@name.to_s) {}
|
||||
end
|
||||
|
||||
it "should convert the string to a symbol" do
|
||||
@factory.factory_name.should == @name
|
||||
end
|
||||
end
|
||||
|
||||
describe "a factory defined with a string name" do
|
||||
before do
|
||||
Factory.factories = {}
|
||||
@name = :user
|
||||
@factory = Factory.define(@name.to_s) {}
|
||||
end
|
||||
|
||||
it "should store the factory using a symbol" do
|
||||
Factory.factories[@name].should == @factory
|
||||
end
|
||||
end
|
||||
|
||||
describe "a factory for namespaced class" do
|
||||
before do
|
||||
Factory.factories = {}
|
||||
@name = :settings
|
||||
@class = Admin::Settings
|
||||
end
|
||||
|
||||
it "should build namespaced class passed by string" do
|
||||
factory = Factory.define(@name.to_s, :class => @class.name) {}
|
||||
factory.build_class.should == @class
|
||||
end
|
||||
|
||||
it "should build Admin::Settings class from Admin::Settings string" do
|
||||
factory = Factory.define(@name.to_s, :class => 'admin/settings') {}
|
||||
factory.build_class.should == @class
|
||||
end
|
||||
end
|
||||
|
||||
describe "after defining a factory" do
|
||||
before do
|
||||
@name = :user
|
||||
@factory = "factory"
|
||||
|
||||
Factory.factories[@name] = @factory
|
||||
end
|
||||
|
||||
after { Factory.factories.clear }
|
||||
|
||||
it "should use Proxy::AttributesFor for Factory.attributes_for" do
|
||||
mock(@factory).run(Factory::Proxy::AttributesFor, :attr => 'value') { 'result' }
|
||||
Factory.attributes_for(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use Proxy::Build for Factory.build" do
|
||||
mock(@factory).run(Factory::Proxy::Build, :attr => 'value') { 'result' }
|
||||
Factory.build(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use Proxy::Create for Factory.create" do
|
||||
mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
|
||||
Factory.create(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use Proxy::Stub for Factory.stub" do
|
||||
mock(@factory).run(Factory::Proxy::Stub, :attr => 'value') { 'result' }
|
||||
Factory.stub(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use default strategy option as Factory.default_strategy" do
|
||||
stub(@factory).default_strategy { :create }
|
||||
mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
|
||||
Factory.default_strategy(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
it "should use the default strategy for the global Factory method" do
|
||||
stub(@factory).default_strategy { :create }
|
||||
mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
|
||||
Factory(@name, :attr => 'value').should == 'result'
|
||||
end
|
||||
|
||||
[:build, :create, :attributes_for, :stub].each do |method|
|
||||
it "should raise an ArgumentError on #{method} with a nonexistant factory" do
|
||||
lambda { Factory.send(method, :bogus) }.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should recognize either 'name' or :name for Factory.#{method}" do
|
||||
stub(@factory).run
|
||||
lambda { Factory.send(method, @name.to_s) }.should_not raise_error
|
||||
lambda { Factory.send(method, @name.to_sym) }.should_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'defining a factory with a parent parameter' do
|
||||
before do
|
||||
@parent = Factory.define :object do |f|
|
||||
f.name 'Name'
|
||||
end
|
||||
end
|
||||
|
||||
after { Factory.factories.clear }
|
||||
|
||||
it "should raise an ArgumentError when trying to use a non-existent factory as parent" do
|
||||
lambda {
|
||||
Factory.define(:child, :parent => :nonexsitent) {}
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should create a new factory using the class of the parent" do
|
||||
child = Factory.define(:child, :parent => :object) {}
|
||||
child.build_class.should == @parent.build_class
|
||||
end
|
||||
|
||||
it "should create a new factory while overriding the parent class" do
|
||||
class Other; end
|
||||
|
||||
child = Factory.define(:child, :parent => :object, :class => Other) {}
|
||||
child.build_class.should == Other
|
||||
end
|
||||
|
||||
it "should create a new factory with attributes of the parent" do
|
||||
child = Factory.define(:child, :parent => :object) {}
|
||||
child.attributes.size.should == 1
|
||||
child.attributes.first.name.should == :name
|
||||
end
|
||||
|
||||
it "should allow to define additional attributes" do
|
||||
child = Factory.define(:child, :parent => :object) do |f|
|
||||
f.email 'person@somebody.com'
|
||||
end
|
||||
child.attributes.size.should == 2
|
||||
end
|
||||
|
||||
it "should allow to override parent attributes" do
|
||||
child = Factory.define(:child, :parent => :object) do |f|
|
||||
f.name { 'Child Name' }
|
||||
end
|
||||
child.attributes.size.should == 1
|
||||
child.attributes.first.should be_kind_of(Factory::Attribute::Dynamic)
|
||||
end
|
||||
|
||||
it "inherit all callbacks" do
|
||||
Factory.define(:child, :parent => :object) do |f|
|
||||
f.after_stub {|o| o.name = 'Stubby' }
|
||||
end
|
||||
|
||||
grandchild = Factory.define(:grandchild, :parent => :child) do |f|
|
||||
f.after_stub {|o| o.name = "#{o.name} McStubby" }
|
||||
end
|
||||
|
||||
grandchild.attributes.size.should == 3
|
||||
grandchild.attributes.first.should be_kind_of(Factory::Attribute::Callback)
|
||||
grandchild.attributes[1].should be_kind_of(Factory::Attribute::Callback)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'defining a factory with a default strategy parameter' do
|
||||
it "should raise an ArgumentError when trying to use a non-existent factory" do
|
||||
lambda {
|
||||
Factory.define(:object, :default_strategy => :nonexistent) {}
|
||||
}.should raise_error(ArgumentError)
|
||||
end
|
||||
|
||||
it "should create a new factory with a specified default strategy" do
|
||||
factory = Factory.define(:object, :default_strategy => :stub) {}
|
||||
factory.default_strategy.should == :stub
|
||||
end
|
||||
|
||||
describe 'defining a child factory without setting default strategy' do
|
||||
before do
|
||||
@child = Factory.define(:child_object, :parent => :object) {}
|
||||
end
|
||||
|
||||
it "should inherit default strategy from its parent" do
|
||||
@child.default_strategy.should == :stub
|
||||
end
|
||||
end
|
||||
|
||||
describe 'defining a child factory with a default strategy' do
|
||||
before do
|
||||
@child2 = Factory.define(:child_object2, :parent => :object, :default_strategy => :build) {}
|
||||
end
|
||||
|
||||
it "should not inherit default strategy from parent" do
|
||||
@child2.default_strategy.should == :build
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "definition loading" do
|
||||
def self.in_directory_with_files(*files)
|
||||
before do
|
||||
@pwd = Dir.pwd
|
||||
|
@ -656,5 +436,4 @@ describe Factory do
|
|||
factory = Factory.new(:name_with_underscores)
|
||||
factory.human_name.should == 'name with underscores'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -10,6 +10,10 @@ require 'factory_girl'
|
|||
|
||||
Spec::Runner.configure do |config|
|
||||
config.mock_with RR::Adapters::Rspec
|
||||
config.after do
|
||||
Factory.factories.clear
|
||||
Factory.sequences.clear
|
||||
end
|
||||
end
|
||||
|
||||
share_as :DefinesConstants do
|
||||
|
|
Loading…
Reference in a new issue