Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Traits can be added to factories when the factory creates an instance

This allows for traits to be used with normal factories without having
to name every single factory that uses one (or many) traits.

So, instead of creating male_admin and female_admin factories:

    FactoryGirl.define do
      factory :user do
        trait(:admin)  { admin true }
        trait(:male)   { gender "Male" }
        trait(:female) { gender "Female" }

        factory :male_admin,   :traits => [:male, :admin]
        factory :female_admin, :traits => [:admin, :female]
      end
    end

    FactoryGirl.create(:male_admin)
    FactoryGirl.create(:female_admin)

You could just create a user with those traits assigned:

    FactoryGirl.create(:user, :admin, :male)
    FactoryGirl.create(:user, :admin, :female)

This can be combined with attribute overrides as expected.

    FactoryGirl.create(:user, :admin, :male,   :name => "John Doe")
    FactoryGirl.create(:user, :admin, :female, :name => "Jane Doe")
  • Loading branch information...
commit 442ba18f14736fd5e197225b67960ac4c846555b 1 parent 845a76a
Joshua Clayton joshuaclayton authored
20 GETTING_STARTED.md
View
@@ -404,6 +404,26 @@ You can also override individual attributes granted by a trait in subclasses.
end
end
+Traits can also be passed in as a list of symbols when you construct an instance from FactoryGirl.
+
+ factory :user do
+ name "Friendly User"
+
+ trait :male do
+ name "John Doe"
+ gender "Male"
+ end
+
+ trait :admin do
+ admin true
+ end
+ end
+
+ # creates an admin user with gender "Male" and name "Jon Snow"
+ FactoryGirl.create(:user, :admin, :male, :name => "Jon Snow")
+
+This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`.
+
Callbacks
---------
11 lib/factory_girl/factory.rb
View
@@ -90,6 +90,12 @@ def compile
@definition.compile
end
+ def with_traits(traits)
+ self.clone.tap do |factory_with_traits|
+ factory_with_traits.inherit_traits traits
+ end
+ end
+
protected
def class_name #:nodoc:
@@ -132,6 +138,11 @@ def parent
end
end
+ def initialize_copy(source)
+ super
+ @definition = @definition.clone
+ end
+
class Runner
def initialize(options = {})
@attributes = options[:attributes]
54 lib/factory_girl/syntax/methods.rb
View
@@ -8,16 +8,17 @@ module Methods
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
- # * overrides: +Hash+
- # Attributes to overwrite for this set.
+ # * traits_and_overrides: +Array+
+ # [+*Array+] Traits to be applied
+ # [+Hash+] Attributes to overwrite for this set.
# * block:
# Yields the hash of attributes.
#
# Returns: +Hash+
# A set of attributes that can be used to build an instance of the class
# this factory generates.
- def attributes_for(name, overrides = {}, &block)
- FactoryGirl.factory_by_name(name).run(Proxy::AttributesFor, overrides, &block)
+ def attributes_for(name, *traits_and_overrides, &block)
+ run_factory_girl_proxy(name, traits_and_overrides, Proxy::AttributesFor, &block)
end
# Generates and returns an instance from this factory. Attributes can be
@@ -26,16 +27,17 @@ def attributes_for(name, overrides = {}, &block)
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
- # * overrides: +Hash+
- # Attributes to overwrite for this instance.
+ # * traits_and_overrides: +Array+
+ # [+*Array+] Traits to be applied
+ # [+Hash+] Attributes to overwrite for this instance.
# * block:
# Yields the built instance.
#
# Returns: +Object+
# An instance of the class this factory generates, with generated attributes
# assigned.
- def build(name, overrides = {}, &block)
- FactoryGirl.factory_by_name(name).run(Proxy::Build, overrides, &block)
+ def build(name, *traits_and_overrides, &block)
+ run_factory_girl_proxy(name, traits_and_overrides, Proxy::Build, &block)
end
# Generates, saves, and returns an instance from this factory. Attributes can
@@ -48,16 +50,17 @@ def build(name, overrides = {}, &block)
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
- # * overrides: +Hash+
- # Attributes to overwrite for this instance.
+ # * traits_and_overrides: +Array+
+ # [+*Array+] Traits to be applied
+ # [+Hash+] Attributes to overwrite for this instance.
# * block:
# Yields the created instance.
#
# Returns: +Object+
# A saved instance of the class this factory generates, with generated
# attributes assigned.
- def create(name, overrides = {}, &block)
- FactoryGirl.factory_by_name(name).run(Proxy::Create, overrides, &block)
+ def create(name, *traits_and_overrides, &block)
+ run_factory_girl_proxy(name, traits_and_overrides, Proxy::Create, &block)
end
# Generates and returns an object with all attributes from this factory
@@ -67,15 +70,16 @@ def create(name, overrides = {}, &block)
# Arguments:
# * name: +Symbol+ or +String+
# The name of the factory that should be used.
- # * overrides: +Hash+
- # Attributes to overwrite for this instance.
+ # * traits_and_overrides: +Array+
+ # [+*Array+] Traits to be applied
+ # [+Hash+] Attributes to overwrite for this instance.
# * block
# Yields the stubbed object.
#
# Returns: +Object+
# An object with generated attributes stubbed out.
- def build_stubbed(name, overrides = {}, &block)
- FactoryGirl.factory_by_name(name).run(Proxy::Stub, overrides, &block)
+ def build_stubbed(name, *traits_and_overrides, &block)
+ run_factory_girl_proxy(name, traits_and_overrides, Proxy::Stub, &block)
end
# Builds and returns multiple instances from this factory as an array. Attributes can be
@@ -125,6 +129,24 @@ def create_list(name, amount, overrides = {})
def generate(name)
FactoryGirl.sequence_by_name(name).next
end
+
+ private
+
+ def run_factory_girl_proxy(name, traits_and_overrides, proxy, &block)
+ overrides = if traits_and_overrides.last.respond_to?(:has_key?)
+ traits_and_overrides.pop
+ else
+ {}
+ end
+
+ factory = FactoryGirl.factory_by_name(name)
+
+ if traits_and_overrides.any?
+ factory = factory.with_traits(traits_and_overrides)
+ end
+
+ factory.run(proxy, overrides, &block)
+ end
end
end
end
54 spec/acceptance/traits_spec.rb
View
@@ -200,3 +200,57 @@
its(:name) { should == "JOHN" }
end
end
+
+describe "traits added via proxy" do
+ before do
+ define_model("User", :name => :string, :admin => :boolean)
+
+ FactoryGirl.define do
+ factory :user do
+ name "John"
+
+ trait :admin do
+ admin true
+ end
+
+ trait :great do
+ after_create {|user| user.name.upcase! }
+ end
+ end
+ end
+ end
+
+ context "adding traits in create" do
+ subject { FactoryGirl.create(:user, :admin, :great, :name => "Joe") }
+
+ its(:admin) { should be_true }
+ its(:name) { should == "JOE" }
+
+ it "doesn't modify the user factory" do
+ subject
+ FactoryGirl.create(:user).should_not be_admin
+ FactoryGirl.create(:user).name.should == "John"
+ end
+ end
+
+ context "adding traits in build" do
+ subject { FactoryGirl.build(:user, :admin, :great, :name => "Joe") }
+
+ its(:admin) { should be_true }
+ its(:name) { should == "Joe" }
+ end
+
+ context "adding traits in attributes_for" do
+ subject { FactoryGirl.attributes_for(:user, :admin, :great) }
+
+ its([:admin]) { should be_true }
+ its([:name]) { should == "John" }
+ end
+
+ context "adding traits in build_stubbed" do
+ subject { FactoryGirl.build_stubbed(:user, :admin, :great, :name => "Jack") }
+
+ its(:admin) { should be_true }
+ its(:name) { should == "Jack" }
+ end
+end
20 spec/factory_girl/factory_spec.rb
View
@@ -286,3 +286,23 @@
block_run.should == "changed"
end
end
+
+describe FactoryGirl::Factory, "#with_traits" do
+ subject { FactoryGirl::Factory.new(:user) }
+ let(:admin_trait) { FactoryGirl::Trait.new(:admin) }
+ let(:female_trait) { FactoryGirl::Trait.new(:female) }
+
+ before do
+ FactoryGirl.register_trait(admin_trait)
+ FactoryGirl.register_trait(female_trait)
+ end
+
+ it "returns a factory with the correct traits" do
+ subject.with_traits([:admin, :female]).traits.should =~ [admin_trait, female_trait]
+ end
+
+ it "doesn't modify the original factory's traits" do
+ subject.with_traits([:admin, :female])
+ subject.traits.should be_empty
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.