Allow child factories to be defined by nesting

This commit is contained in:
Joshua Clayton 2011-06-29 16:49:45 -04:00
parent 4d8d419375
commit 0c0699759f
5 changed files with 56 additions and 23 deletions

View File

@ -133,7 +133,21 @@ The behavior of the association method varies depending on the build strategy us
Inheritance
-----------
You can easily create multiple factories for the same class without repeating common attributes by using inheritance:
You can easily create multiple factories for the same class without repeating common attributes by nesting factories:
factory :post do
title 'A title'
factory :approved_post do
approved true
end
end
approved_post = FactoryGirl.create(:approved_post)
approved_post.title # => 'A title'
approved_post.approved # => true
You can also assign the parent explicitly:
factory :post do
title 'A title'
@ -143,9 +157,11 @@ You can easily create multiple factories for the same class without repeating co
approved true
end
FactoryGirl.create(:approved_post).title # => 'A title'
approved_post = FactoryGirl.create(:approved_post)
approved_post.title # => 'A title'
approved_post.approved # => true
As mentioned above, it's good practice to define a basic factory for each class with only the attributes required to create it. Then, create more-specific factories that inherit from this basic parent. Factory definitions are still code, so keep them DRY.
As mentioned above, it's good practice to define a basic factory for each class with only the attributes required to create it. Then, create more specific factories that inherit from this basic parent. Factory definitions are still code, so keep them DRY.
Sequences
---------

View File

@ -4,8 +4,11 @@ module FactoryGirl
undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$|^instance_eval$)/
end
attr_reader :child_factories
def initialize(factory)
@factory = factory
@child_factories = []
end
# Adds an attribute that should be assigned on generated instances for this
@ -143,5 +146,9 @@ module FactoryGirl
def to_create(&block)
@factory.to_create(&block)
end
def factory(name, options = {}, &block)
@child_factories << [name, options, block]
end
end
end

View File

@ -20,6 +20,10 @@ module FactoryGirl
factory.inherit_from(FactoryGirl.factory_by_name(parent))
end
FactoryGirl.register_factory(factory)
proxy.child_factories.each do |(child_name, child_options, child_block)|
factory(child_name, child_options.merge(:parent => name), &child_block)
end
end
def sequence(name, start_value = 1, &block)

View File

@ -7,33 +7,29 @@ describe "an instance generated by a factory that inherits from another factory"
FactoryGirl.define do
factory :user do
name { "John" }
email { "john@example.com" }
end
name "John"
email "john@example.com"
factory :admin, :parent => :user do
admin { true }
email { "admin@example.com" }
factory :admin do
admin true
email "admin@example.com"
end
end
end
end
subject { FactoryGirl.create(:admin) }
it "uses the parent build class" do
subject.should be_kind_of(User)
describe "the parent class" do
subject { FactoryGirl.create(:user) }
it { should_not be_admin }
its(:email) { should == "john@example.com" }
end
it "inherits attributes" do
subject.name.should == 'John'
end
it "has its own attributes" do
subject.should be_admin
end
it "overrides attributes" do
subject.email.should == 'admin@example.com'
describe "the child class" do
subject { FactoryGirl.create(:admin) }
it { should be_kind_of(User) }
it { should be_admin }
its(:name) { should == "John" }
its(:email) { should == "admin@example.com" }
end
end

View File

@ -37,6 +37,16 @@ describe FactoryGirl::DefinitionProxy do
}.should raise_error(FactoryGirl::AttributeDefinitionError)
end
describe "child factories" do
its(:child_factories) { should == [] }
it "should be able to add child factories" do
block = lambda {}
subject.factory(:admin, { :aliases => [:great] }, &block)
subject.child_factories.should == [[:admin, { :aliases => [:great] }, block]]
end
end
describe "adding an attribute using a in-line sequence" do
it "should create the sequence" do
mock(FactoryGirl::Sequence).new(:name, 1)