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:
parent
306e51b91d
commit
a154e64da1
20 changed files with 330 additions and 170 deletions
|
@ -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
|
||||
-------------------------------------
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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}"
|
||||
|
|
19
lib/factory_girl/declaration.rb
Normal file
19
lib/factory_girl/declaration.rb
Normal 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
|
17
lib/factory_girl/declaration/association.rb
Normal file
17
lib/factory_girl/declaration/association.rb
Normal 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
|
16
lib/factory_girl/declaration/dynamic.rb
Normal file
16
lib/factory_girl/declaration/dynamic.rb
Normal 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
|
23
lib/factory_girl/declaration/implicit.rb
Normal file
23
lib/factory_girl/declaration/implicit.rb
Normal 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
|
16
lib/factory_girl/declaration/static.rb
Normal file
16
lib/factory_girl/declaration/static.rb
Normal 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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
52
spec/acceptance/modify_inherited_spec.rb
Normal file
52
spec/acceptance/modify_inherited_spec.rb
Normal 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
|
|
@ -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) {}
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue