Skip to content

Commit

Permalink
Allow child factories to be defined by nesting
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuaclayton committed Jun 29, 2011
1 parent 4d8d419 commit 0c06997
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 23 deletions.
22 changes: 19 additions & 3 deletions GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -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
---------
Expand Down
7 changes: 7 additions & 0 deletions lib/factory_girl/definition_proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ class DefinitionProxy
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
Expand Down Expand Up @@ -143,5 +146,9 @@ def after_stub(&block)
def to_create(&block)
@factory.to_create(&block)
end

def factory(name, options = {}, &block)
@child_factories << [name, options, block]
end
end
end
4 changes: 4 additions & 0 deletions lib/factory_girl/syntax/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def factory(name, options = {}, &block)
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)
Expand Down
36 changes: 16 additions & 20 deletions spec/acceptance/parent_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,29 @@

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)
end

it "inherits attributes" do
subject.name.should == 'John'
end

it "has its own attributes" do
subject.should be_admin
describe "the parent class" do
subject { FactoryGirl.create(:user) }
it { should_not be_admin }
its(:email) { should == "john@example.com" }
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

10 changes: 10 additions & 0 deletions spec/factory_girl/definition_proxy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@
}.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)
Expand Down

0 comments on commit 0c06997

Please sign in to comment.