diff --git a/lib/factory_girl/factory.rb b/lib/factory_girl/factory.rb index 299a80c..c839b68 100644 --- a/lib/factory_girl/factory.rb +++ b/lib/factory_girl/factory.rb @@ -12,19 +12,36 @@ class Factory # Arguments: # name: (Symbol) # A unique name used to identify this factory. + # options: (Hash) + # class: the class that will be used when generating instances for this + # factory. # # Yields: # The newly created factory (Factory) - def self.define (name) - instance = Factory.new(name) + def self.define (name, options = {}) + instance = Factory.new(name, options) yield(instance) self.factories << instance end - def initialize (name) #:nodoc: - @name = name + # Calculates the class that should be instantiated by generation methods. + # + # If a class was passed when defining this factory, that class will be + # returned. Otherwise, the class will be guessed from the factory name. + # + # Returns: + # The class that will be instantiated by generation methods. + def build_class + @build_class ||= @options[:class] || name.to_s.classify.constantize + end + + def initialize (name, options = {}) #:nodoc: + options.assert_valid_keys(:class) + @name = name + @options = options + @static_attributes = {} - @lazy_attributes = {} + @lazy_attributes = {} end # Adds an attribute that should be assigned or stubbed on generated instances for this factory. @@ -72,4 +89,42 @@ class Factory result.update(attrs) end + # Generates and returns an instance from this factory. Attributes can be + # individually overridden by passing in a Hash of attribute => value pairs. + # + # Arguments: + # attrs: (Hash) + # See attributes + # + # Returns: + # An instance of the class this factory generates, with generated + # attributes assigned. + def build (attrs = {}) + instance = build_class.new + attributes(attrs).each do |attr, value| + instance.send(:"#{attr}=", value) + end + instance + end + + # Generates, saves, and returns an instance from this factory. Attributes can + # be individually overridden by passing in a Hash of attribute => value + # pairs. + # + # If the instance is not valid, an ActiveRecord::Invalid exception will be + # raised. + # + # Arguments: + # attrs: (Hash) + # See attributes + # + # Returns: + # A saved instance of the class this factory generates, with generated + # attributes assigned. + def create (attrs = {}) + instance = build(attrs) + instance.save! + instance + end + end diff --git a/test/factory_test.rb b/test/factory_test.rb index fa86f1e..ff549ee 100644 --- a/test/factory_test.rb +++ b/test/factory_test.rb @@ -2,17 +2,37 @@ require(File.join(File.dirname(__FILE__), 'test_helper')) class FactoryTest < Test::Unit::TestCase + def self.should_instantiate_class + + should "instantiate the build class" do + assert_kind_of @class, @instance + end + + should "assign attributes on the instance" do + assert_equal @first_name, @instance.first_name + assert_equal @last_name, @instance.last_name + end + + should "override attributes using the passed hash" do + @value = 'Davis' + @instance = @factory.build(:first_name => @value) + assert_equal @value, @instance.first_name + end + + end + context "defining a factory" do setup do @name = :user @factory = mock('factory') + @options = { :class => 'magic' } Factory.stubs(:new).returns(@factory) end - should "create a new factory" do - Factory.expects(:new).with(@name) - Factory.define(@name) {|f| } + should "create a new factory using the specified name and options" do + Factory.expects(:new).with(@name, @options) + Factory.define(@name, @options) {|f| } end should "pass the factory do the block" do @@ -35,6 +55,7 @@ class FactoryTest < Test::Unit::TestCase setup do @name = :user + @class = User @factory = Factory.new(@name) end @@ -42,6 +63,10 @@ class FactoryTest < Test::Unit::TestCase assert_equal @name, @factory.name end + should "have a build class" do + assert_equal @class, @factory.build_class + end + context "when adding an attribute with a value parameter" do setup do @@ -109,6 +134,68 @@ class FactoryTest < Test::Unit::TestCase end + should "guess the build class from the factory name" do + assert_equal User, @factory.build_class + end + + context "when defined with a custom class" do + + setup do + @class = User + @factory = Factory.new(:author, :class => @class) + end + + should "use the specified class as the build class" do + assert_equal @class, @factory.build_class + end + + end + + context "with some attributes added" do + + setup do + @first_name = 'Billy' + @last_name = 'Idol' + @email = 'test@something.com' + + @factory.add_attribute(:first_name, @first_name) + @factory.add_attribute(:last_name, @last_name) + @factory.add_attribute(:email, @email) + end + + context "when building an instance" do + + setup do + @instance = @factory.build + end + + should_instantiate_class + + end + + context "when creating an instance" do + + setup do + User.delete_all + @instance = @factory.create + end + + should_instantiate_class + + should "save the instance" do + assert_equal 1, @class.count + end + + end + + should "raise an ActiveRecord::RecordInvalid error for invalid instances" do + assert_raise(ActiveRecord::RecordInvalid) do + @factory.create(:first_name => nil) + end + end + + end + end end