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

Introduce declarations

Declarations are another layer of abstraction between defining the
factories via the DSL and compiling the factories and their attributes.

Declarations know how to return their attribute(s), and running a
factory compiles the declarations before building all attributes on the
factory. This moves all the attribute compilation logic into one
centralized location on the Factory instance, which means traits (and
potentially other features down the road) can have individual attributes
overridden within child factories or through FactoryGirl.modify

Closes #205
This commit is contained in:
Joe Ferris and Josh Clayton 2011-09-23 13:14:02 -04:00 committed by Joshua Clayton
parent 306e51b91d
commit a154e64da1
20 changed files with 330 additions and 170 deletions

View file

@ -379,6 +379,24 @@ the trait that defines the attribute latest gets precedence.
factory :female_admin, :traits => [:admin, :female] # login will be "Jane Doe (F)"
end
You can also override individual attributes granted by a trait in subclasses.
factory :user do
name "Friendly User"
login { name }
trait :male do
name "John Doe"
gender "Male"
login { "#{name} (M)" }
end
factory :brandon do
male
name "Brandon"
end
end
Callbacks
---------
@ -457,10 +475,8 @@ 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.
A couple caveats: 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.
You also can't modify attributes assigned by traits. So, if you have a trait that grants a name attribute, and you modify the factory to set the name,
it currently will reflect the name in the trait instead of the modified name.
Building or Creating Multiple Records
-------------------------------------

View file

@ -10,9 +10,12 @@ require 'factory_girl/attribute/static'
require 'factory_girl/attribute/dynamic'
require 'factory_girl/attribute/association'
require 'factory_girl/attribute/sequence'
require 'factory_girl/attribute/implicit'
require 'factory_girl/attribute/trait'
require 'factory_girl/callback'
require 'factory_girl/declaration'
require 'factory_girl/declaration/static'
require 'factory_girl/declaration/dynamic'
require 'factory_girl/declaration/association'
require 'factory_girl/declaration/implicit'
require 'factory_girl/sequence'
require 'factory_girl/attribute_list'
require 'factory_girl/trait'

View file

@ -1,37 +0,0 @@
module FactoryGirl
class Attribute
class Implicit < Attribute
def initialize(name, factory = nil)
super(name)
@factory = factory
end
def add_to(proxy)
implementation.add_to(proxy)
end
def association?
implementation.association?
end
def factory
name
end
private
def implementation
@implementation ||=
if FactoryGirl.factories.registered?(name)
Attribute::Association.new(name, name, {})
elsif FactoryGirl.sequences.registered?(name)
Attribute::Sequence.new(name, name)
else
Attribute::Trait.new(name, @factory)
end
end
end
end
end

View file

@ -3,6 +3,8 @@ module FactoryGirl
class Static < Attribute #:nodoc:
attr_reader :value
def initialize(name, value)
super(name)
@value = value
@ -15,6 +17,12 @@ module FactoryGirl
def priority
0
end
def ==(another)
self.name == another.name &&
self.value == another.value &&
self.ignored == another.ignored
end
end
end
end

View file

@ -1,22 +0,0 @@
module FactoryGirl
class Attribute #:nodoc:
class Trait < Attribute #:nodoc:
def initialize(name, factory)
super(name)
@factory = factory
end
def add_to(proxy)
trait.attributes.each { |attr| attr.add_to(proxy) }
end
private
def trait
(@factory || FactoryGirl).trait_by_name(name)
end
end
end
end

View file

@ -2,14 +2,19 @@ module FactoryGirl
class AttributeList
include Enumerable
attr_reader :callbacks
attr_reader :callbacks, :declarations
def initialize
@attributes = {}
@declarations = []
@overridable = false
@callbacks = []
end
def declare_attribute(declaration)
@declarations << declaration
end
def define_attribute(attribute)
if !overridable? && attribute_defined?(attribute.name)
raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"

View file

@ -0,0 +1,19 @@
module FactoryGirl
class Declaration
attr_reader :name
def initialize(name)
@name = name
end
def ignore
@ignored = true
end
def to_attributes
build.tap do |attributes|
attributes.each(&:ignore) if @ignored
end
end
end
end

View file

@ -0,0 +1,17 @@
module FactoryGirl
class Declaration
class Association < Declaration
def initialize(name, options)
super(name)
@options = options
end
private
def build
factory_name = @options.delete(:factory) || name
[Attribute::Association.new(name, factory_name, @options)]
end
end
end
end

View file

@ -0,0 +1,16 @@
module FactoryGirl
class Declaration
class Dynamic < Declaration
def initialize(name, block)
super(name)
@block = block
end
private
def build
[Attribute::Dynamic.new(name, @block)]
end
end
end
end

View file

@ -0,0 +1,23 @@
module FactoryGirl
class Declaration
class Implicit < Declaration
def initialize(name, factory = nil)
super(name)
@factory = factory
end
private
def build
if FactoryGirl.factories.registered?(name)
[Attribute::Association.new(name, name, {})]
elsif FactoryGirl.sequences.registered?(name)
[Attribute::Sequence.new(name, name)]
else
trait_root = @factory || FactoryGirl
trait_root.trait_by_name(name).attributes.to_a
end
end
end
end
end

View file

@ -0,0 +1,16 @@
module FactoryGirl
class Declaration
class Static < Declaration
def initialize(name, value)
super(name)
@value = value
end
private
def build
[Attribute::Static.new(name, @value)]
end
end
end
end

View file

@ -36,13 +36,14 @@ module FactoryGirl
if value
raise AttributeDefinitionError, "Both value and block given"
else
attribute = Attribute::Dynamic.new(name, block)
declaration = Declaration::Dynamic.new(name, block)
end
else
attribute = Attribute::Static.new(name, value)
declaration = FactoryGirl::Declaration::Static.new(name, value)
end
@factory.define_attribute(attribute)
@factory.declare_attribute(declaration)
declaration
end
# Calls add_attribute using the missing method name as the name of the
@ -78,7 +79,7 @@ module FactoryGirl
# are equivalent.
def method_missing(name, *args, &block)
if args.empty? && block.nil?
@factory.define_attribute(Attribute::Implicit.new(name, @factory))
@factory.declare_attribute(Declaration::Implicit.new(name, @factory))
elsif args.first.is_a?(Hash) && args.first.has_key?(:factory)
association(name, *args)
else
@ -131,8 +132,7 @@ module FactoryGirl
# 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))
@factory.declare_attribute(Declaration::Association.new(name, options))
end
def after_build(&block)

View file

@ -13,7 +13,6 @@ module FactoryGirl
class Factory
attr_reader :name #:nodoc:
attr_reader :traits #:nodoc:
def factory_name
puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
@ -34,18 +33,19 @@ module FactoryGirl
def initialize(name, options = {}) #:nodoc:
assert_valid_options(options)
@name = factory_name_for(name)
@parent = options[:parent]
@options = options
@traits = []
@children = []
@attribute_list = AttributeList.new
@inherited_attribute_list = AttributeList.new
@name = factory_name_for(name)
@parent = options[:parent]
@parent_factory = nil
@options = options
@defined_traits = []
@traits = []
@children = []
@attribute_list = AttributeList.new
@compiled = false
end
def allow_overrides
@attribute_list.overridable
@inherited_attribute_list.overridable
self
end
@ -53,40 +53,26 @@ module FactoryGirl
@attribute_list.overridable?
end
def inherit_from(parent) #:nodoc:
def inherit_factory(parent) #:nodoc:
@options[:class] ||= parent.class_name
@options[:default_strategy] ||= parent.default_strategy
allow_overrides if parent.allow_overrides?
parent.add_child(self)
@inherited_attribute_list.apply_attributes(parent.attributes)
@parent_factory = parent
end
def add_child(factory)
@children << factory unless @children.include?(factory)
end
def apply_traits(traits) #:nodoc:
traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
apply_attributes(trait.attributes)
end
end
def apply_attributes(attributes_to_apply)
@attribute_list.apply_attributes(attributes_to_apply)
end
def define_attribute(attribute)
if attribute.respond_to?(:factory) && attribute.factory == self.name
raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in factory '#{self.name}'"
end
@attribute_list.define_attribute(attribute).tap { update_children }
def inherit_traits(traits)
@traits = traits
end
def define_trait(trait)
@traits << trait
@defined_traits << trait
end
def add_callback(name, &block)
@ -94,9 +80,14 @@ module FactoryGirl
end
def attributes
ensure_compiled
AttributeList.new.tap do |list|
@traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
list.apply_attributes(trait.attributes)
end
list.apply_attributes(@attribute_list)
list.apply_attributes(@inherited_attribute_list)
list.apply_attributes(@parent_factory.attributes) if @parent_factory
end
end
@ -172,10 +163,36 @@ module FactoryGirl
attributes.callbacks
end
def compile
declarations.each do |declaration|
declaration.to_attributes.each do |attribute|
define_attribute(attribute)
end
end
@compiled = true
end
def declare_attribute(declaration)
@attribute_list.declare_attribute(declaration)
end
private
def declarations
@attribute_list.declarations
end
def update_children
@children.each { |child| child.inherit_from(self) }
@children.each { |child| child.inherit_factory(self) }
end
def define_attribute(attribute)
if attribute.respond_to?(:factory) && attribute.factory == self.name
raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in factory '#{self.name}'"
end
@attribute_list.define_attribute(attribute)
update_children if allow_overrides?
end
def class_for (class_or_to_s)
@ -240,7 +257,11 @@ module FactoryGirl
end
def trait_for(name)
traits.detect {|trait| trait.name == name }
@defined_traits.detect {|trait| trait.name == name }
end
def ensure_compiled
compile unless @compiled
end
end
end

View file

@ -22,12 +22,13 @@ module FactoryGirl
proxy.instance_eval(&block) if block_given?
if traits = options.delete(:traits)
factory.apply_traits(traits)
factory.inherit_traits(traits)
end
if parent = options.delete(:parent)
factory.inherit_from(FactoryGirl.factory_by_name(parent))
factory.inherit_factory(FactoryGirl.factory_by_name(parent))
end
FactoryGirl.register_factory(factory)
proxy.child_factories.each do |(child_name, child_options, child_block)|
@ -53,6 +54,7 @@ module FactoryGirl
factory = FactoryGirl.factory_by_name(name).allow_overrides
proxy = FactoryGirl::DefinitionProxy.new(factory)
proxy.instance_eval(&block)
factory.compile
end
end
end

View file

@ -10,8 +10,9 @@ module FactoryGirl
proxy.instance_eval(&block) if block_given?
end
def define_attribute(attribute)
@attribute_list.define_attribute(attribute)
def declare_attribute(declaration)
@attribute_list.declare_attribute(declaration)
declaration
end
def add_callback(name, &block)
@ -19,7 +20,14 @@ module FactoryGirl
end
def attributes
@attribute_list
AttributeList.new.tap do |list|
@attribute_list.declarations.each do |declaration|
declaration.to_attributes.each do |attribute|
list.define_attribute(attribute)
end
end
list.apply_attributes @attribute_list
end
end
def names

View file

@ -0,0 +1,52 @@
require "spec_helper"
describe "modifying inherited factories with traits" do
before do
define_model('User', :gender => :string, :admin => :boolean, :age => :integer)
FactoryGirl.define do
factory :user do
trait(:female) { gender "Female" }
trait(:male) { gender "Male" }
trait(:young_admin) do
admin true
age 17
end
female
young_admin
factory :female_user do
gender "Female"
age 25
end
factory :male_user do
gender "Male"
end
end
end
end
it "returns the correct value for overridden attributes from traits" do
Factory.build(:male_user).gender.should == "Male"
end
it "returns the correct value for overridden attributes from traits defining multiple attributes" do
Factory.build(:female_user).gender.should == "Female"
Factory.build(:female_user).age.should == 25
Factory.build(:female_user).admin.should == true
end
it "allows modification of attributes created via traits" do
FactoryGirl.modify do
factory :male_user do
age 20
end
end
Factory.build(:male_user).gender.should == "Male"
Factory.build(:male_user).age.should == 20
Factory.build(:male_user).admin.should == true
end
end

View file

@ -46,13 +46,7 @@ describe "vintage syntax" do
end
end
describe Factory, "given a parent factory" do
before do
@parent = FactoryGirl::Factory.new(:object)
@parent.define_attribute(FactoryGirl::Attribute::Static.new(:name, 'value'))
FactoryGirl.register_factory(@parent)
end
describe Factory, "referencing a nonexistent factory as a parent" do
it "should raise an ArgumentError when trying to use a non-existent factory as parent" do
lambda {
Factory.define(:child, :parent => :nonexsitent) {}

View file

@ -1,24 +1,27 @@
require 'spec_helper'
describe FactoryGirl::Attribute::Implicit do
let(:name) { :author }
let(:proxy) { stub("proxy") }
subject { FactoryGirl::Attribute::Implicit.new(name) }
its(:name) { should == name }
describe FactoryGirl::Declaration::Implicit do
let(:name) { :author }
let(:proxy) { stub("proxy") }
subject { FactoryGirl::Declaration::Implicit.new(name) }
let(:attribute) { subject.to_attributes.first }
context "with a known factory" do
before do
FactoryGirl.factories.stubs(:registered? => true)
end
it { should be_association }
it "generates an association" do
attribute.should be_association
end
its(:factory) { should == name }
it "generates an association with the correct factory" do
attribute.factory.should == name
end
it "associates the factory" do
proxy.stubs(:associate)
subject.add_to(proxy)
attribute.add_to(proxy)
proxy.should have_received(:associate).with(name, name, {})
end
end
@ -27,11 +30,13 @@ describe FactoryGirl::Attribute::Implicit do
let(:sequence) { FactoryGirl::Sequence.new(name, 1) { "magic" } }
before { FactoryGirl.register_sequence(sequence) }
it { should_not be_association }
it "doesn't generate an association" do
attribute.should_not be_association
end
it "generates the sequence" do
proxy.stubs(:set)
subject.add_to(proxy)
attribute.add_to(proxy)
proxy.should have_received(:set).with(name, "magic")
end
end

View file

@ -4,14 +4,18 @@ describe FactoryGirl::DefinitionProxy do
let(:factory) { FactoryGirl::Factory.new(:object) }
subject { FactoryGirl::DefinitionProxy.new(factory) }
def attributes
factory.attributes
end
it "should add a static attribute for type" do
subject.type 'value'
factory.attributes.to_a.last.should be_kind_of(FactoryGirl::Attribute::Static)
attributes.to_a.last.should be_kind_of(FactoryGirl::Attribute::Static)
end
it "should add a static attribute for id" do
subject.id 'value'
factory.attributes.to_a.last.should be_kind_of(FactoryGirl::Attribute::Static)
attributes.to_a.last.should be_kind_of(FactoryGirl::Attribute::Static)
end
it "should add a static attribute when an attribute is defined with a value" do
@ -19,6 +23,7 @@ describe FactoryGirl::DefinitionProxy do
FactoryGirl::Attribute::Static.stubs(:new => attribute)
factory.stubs(:define_attribute)
subject.add_attribute(:name, 'value')
factory.compile
factory.should have_received(:define_attribute).with(attribute)
FactoryGirl::Attribute::Static.should have_received(:new).with(:name, "value")
end
@ -29,6 +34,7 @@ describe FactoryGirl::DefinitionProxy do
FactoryGirl::Attribute::Dynamic.stubs(:new => attribute)
factory.stubs(:define_attribute)
subject.add_attribute(:name, &block)
factory.compile
FactoryGirl::Attribute::Dynamic.should have_received(:new).with(:name, block)
factory.should have_received(:define_attribute).with(attribute)
end
@ -41,7 +47,7 @@ describe FactoryGirl::DefinitionProxy do
it "should add an attribute with a built-in private method" do
subject.instance_eval { sleep(0.1) }
factory.attributes.map { |attribute| attribute.name }.should == [:sleep]
attributes.map { |attribute| attribute.name }.should == [:sleep]
end
describe "child factories" do
@ -67,15 +73,6 @@ describe FactoryGirl::DefinitionProxy do
FactoryGirl::Sequence.should have_received(:new).with(:name, "A")
end
end
it "adds an implicit attribute when passed an undefined method without arguments or a block" do
factory.stubs(:define_attribute)
attribute = stub('attribute', :name => :name)
FactoryGirl::Attribute::Implicit.stubs(:new => attribute)
subject.send(:name)
FactoryGirl::Attribute::Implicit.should have_received(:new).with(:name, factory)
factory.should have_received(:define_attribute).with(attribute)
end
end
describe FactoryGirl::DefinitionProxy, "with a factory mock" do
@ -126,12 +123,14 @@ describe FactoryGirl::DefinitionProxy, "adding attributes" do
it "creates a dynamic attribute" do
subject.add_attribute(attribute_name, &block)
factory.compile
FactoryGirl::Attribute::Dynamic.should have_received(:new).with(attribute_name, block)
factory.should have_received(:define_attribute).with(attribute)
end
it "creates a dynamic attribute without the method being defined" do
subject.send(attribute_name, &block)
factory.compile
FactoryGirl::Attribute::Dynamic.should have_received(:new).with(attribute_name, block)
factory.should have_received(:define_attribute).with(attribute)
end
@ -142,12 +141,14 @@ describe FactoryGirl::DefinitionProxy, "adding attributes" do
it "creates a static attribute" do
subject.add_attribute(attribute_name, attribute_value)
factory.compile
FactoryGirl::Attribute::Static.should have_received(:new).with(attribute_name, attribute_value)
factory.should have_received(:define_attribute).with(attribute)
end
it "creates a static attribute without the method being defined" do
subject.send(attribute_name, attribute_value)
factory.compile
FactoryGirl::Attribute::Static.should have_received(:new).with(attribute_name, attribute_value)
factory.should have_received(:define_attribute).with(attribute)
end
@ -179,6 +180,7 @@ describe FactoryGirl::DefinitionProxy, "#association" do
it "defines an association attribute with the factory name" do
subject.association(association_name, options)
factory.compile
factory.should have_received(:define_attribute).with(attribute)
FactoryGirl::Attribute::Association.should have_received(:new).with(association_name, factory_name, :name => "John Doe")
@ -186,6 +188,7 @@ describe FactoryGirl::DefinitionProxy, "#association" do
it "defines an association attribute when the association is called implicitly" do
subject.send(association_name, options)
factory.compile
factory.should have_received(:define_attribute).with(attribute)
FactoryGirl::Attribute::Association.should have_received(:new).with(association_name, factory_name, :name => "John Doe")
@ -197,6 +200,7 @@ describe FactoryGirl::DefinitionProxy, "#association" do
it "defines an association attribute with the association name" do
subject.association(association_name, options)
factory.compile
factory.should have_received(:define_attribute).with(attribute)
FactoryGirl::Attribute::Association.should have_received(:new).with(association_name, association_name, options)

View file

@ -38,9 +38,9 @@ describe FactoryGirl::Factory do
it "should return associations" do
factory = FactoryGirl::Factory.new(:post)
FactoryGirl.register_factory(FactoryGirl::Factory.new(:admin))
factory.define_attribute(FactoryGirl::Attribute::Association.new(:author, :author, {}))
factory.define_attribute(FactoryGirl::Attribute::Association.new(:editor, :editor, {}))
factory.define_attribute(FactoryGirl::Attribute::Implicit.new(:admin))
factory.declare_attribute(FactoryGirl::Declaration::Association.new(:author, {}))
factory.declare_attribute(FactoryGirl::Declaration::Association.new(:editor, {}))
factory.declare_attribute(FactoryGirl::Declaration::Implicit.new(:admin, factory))
factory.associations.each do |association|
association.should be_association
end
@ -50,7 +50,8 @@ describe FactoryGirl::Factory do
it "should raise for a self referencing association" do
factory = FactoryGirl::Factory.new(:post)
lambda {
factory.define_attribute(FactoryGirl::Attribute::Association.new(:parent, :post, {}))
factory.declare_attribute(FactoryGirl::Declaration::Association.new(:parent, { :factory => :post }))
factory.attributes
}.should raise_error(FactoryGirl::AssociationDefinitionError)
end
@ -62,21 +63,21 @@ describe FactoryGirl::Factory do
end
it "should return the overridden value in the generated attributes" do
attr = FactoryGirl::Attribute::Static.new(@name, 'The price is wrong, Bob!')
@factory.define_attribute(attr)
declaration = FactoryGirl::Declaration::Static.new(@name, 'The price is wrong, Bob!')
@factory.declare_attribute(declaration)
result = @factory.run(FactoryGirl::Proxy::AttributesFor, @hash)
result[@name].should == @value
end
it "should not call a lazy attribute block for an overridden attribute" do
attr = FactoryGirl::Attribute::Dynamic.new(@name, lambda { flunk })
@factory.define_attribute(attr)
declaration = FactoryGirl::Declaration::Dynamic.new(@name, lambda { flunk })
@factory.declare_attribute(declaration)
result = @factory.run(FactoryGirl::Proxy::AttributesFor, @hash)
end
it "should override a symbol parameter with a string parameter" do
attr = FactoryGirl::Attribute::Static.new(@name, 'The price is wrong, Bob!')
@factory.define_attribute(attr)
declaration = FactoryGirl::Declaration::Static.new(@name, 'The price is wrong, Bob!')
@factory.declare_attribute(declaration)
@hash = { @name.to_s => @value }
result = @factory.run(FactoryGirl::Proxy::AttributesFor, @hash)
result[@name].should == @value
@ -85,7 +86,7 @@ describe FactoryGirl::Factory do
describe "overriding an attribute with an alias" do
before do
@factory.define_attribute(FactoryGirl::Attribute::Static.new(:test, 'original'))
@factory.declare_attribute(FactoryGirl::Declaration::Static.new(:test, 'original'))
Factory.alias(/(.*)_alias/, '\1')
@result = @factory.run(FactoryGirl::Proxy::AttributesFor,
:test_alias => 'new')
@ -106,52 +107,52 @@ describe FactoryGirl::Factory do
it "should create a new factory using the class of the parent" do
child = FactoryGirl::Factory.new(:child)
child.inherit_from(@factory)
child.inherit_factory(@factory)
child.build_class.should == @factory.build_class
end
it "should create a new factory while overriding the parent class" do
child = FactoryGirl::Factory.new(:child, :class => String)
child.inherit_from(@factory)
child.inherit_factory(@factory)
child.build_class.should == String
end
describe "given a parent with attributes" do
before do
@parent_attr = :name
@factory.define_attribute(FactoryGirl::Attribute::Static.new(@parent_attr, 'value'))
@factory.declare_attribute(FactoryGirl::Declaration::Static.new(@parent_attr, 'value'))
end
it "should create a new factory with attributes of the parent" do
child = FactoryGirl::Factory.new(:child)
child.inherit_from(@factory)
child.inherit_factory(@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 = FactoryGirl::Factory.new(:child)
child.define_attribute(FactoryGirl::Attribute::Static.new(:email, 'value'))
child.inherit_from(@factory)
child.declare_attribute(FactoryGirl::Declaration::Static.new(:email, 'value'))
child.inherit_factory(@factory)
child.attributes.size.should == 2
end
it "should allow to override parent attributes" do
child = FactoryGirl::Factory.new(:child)
@child_attr = FactoryGirl::Attribute::Static.new(@parent_attr, 'value')
child.define_attribute(@child_attr)
child.inherit_from(@factory)
@child_declaration = FactoryGirl::Declaration::Static.new(@parent_attr, 'overridden')
child.declare_attribute(@child_declaration)
child.inherit_factory(@factory)
child.attributes.size.should == 1
child.attributes.first.should == @child_attr
child.attributes.first.should == @child_declaration.to_attributes.first
end
it "should allow to use parent attributes in defining additional attributes" do
User.class_eval { attr_accessor :name, :email }
child = FactoryGirl::Factory.new(:child)
@child_attr = FactoryGirl::Attribute::Dynamic.new(:email, lambda {|u| "#{u.name}@example.com"})
child.define_attribute(@child_attr)
child.inherit_from(@factory)
@child_declaration = FactoryGirl::Declaration::Dynamic.new(:email, lambda {|u| "#{u.name}@example.com"})
child.declare_attribute(@child_declaration)
child.inherit_factory(@factory)
child.attributes.size.should == 2
result = child.run(FactoryGirl::Proxy::Build, {})
@ -162,9 +163,14 @@ describe FactoryGirl::Factory do
it "inherits callbacks" do
@factory.add_callback(:after_stub) { |object| object.name = 'Stubby' }
child = FactoryGirl::Factory.new(:child)
child.inherit_from(@factory)
child.inherit_factory(@factory)
child.callbacks.should_not be_empty
end
it "compiles when looking for attributes" do
@factory.declare_attribute(FactoryGirl::Declaration::Static.new("name", "value"))
@factory.attributes.size.should == 1
end
end
describe FactoryGirl::Factory, "when defined with a custom class" do
@ -265,7 +271,7 @@ describe FactoryGirl::Factory do
subject { factory_without_strategy }
before do
subject.inherit_from(parent)
subject.inherit_factory(parent)
end
it "inherits default strategy from its parent" do
@ -278,7 +284,7 @@ describe FactoryGirl::Factory do
subject { factory_with_build_strategy }
before do
subject.inherit_from(parent)
subject.inherit_factory(parent)
end
it "overrides the default strategy from parent" do
@ -308,16 +314,20 @@ describe FactoryGirl::Factory, "human names" do
end
describe FactoryGirl::Factory, "running a factory" do
subject { FactoryGirl::Factory.new(:user) }
let(:attribute) { FactoryGirl::Attribute::Static.new(:name, "value") }
let(:proxy) { stub("proxy", :result => "result", :set => nil) }
subject { FactoryGirl::Factory.new(:user) }
let(:attribute) { FactoryGirl::Attribute::Static.new(:name, "value") }
let(:declaration) { FactoryGirl::Declaration::Static.new(:name, "value") }
let(:proxy) { stub("proxy", :result => "result", :set => nil) }
let(:attributes) { [attribute] }
let(:attribute_list) { stub('attribute-list', :declarations => [declaration], :to_a => attributes) }
before do
define_model("User", :name => :string)
FactoryGirl::Attribute::Static.stubs(:new => attribute)
FactoryGirl::Proxy::Build.stubs(:new => proxy)
subject.define_attribute(attribute)
FactoryGirl::Declaration::Static.stubs(:new => declaration)
declaration.stubs(:to_attributes => attributes)
attribute.stubs(:add_to => nil)
FactoryGirl::Proxy::Build.stubs(:new => proxy)
subject.declare_attribute(declaration)
end
it "creates the right proxy using the build class when running" do