require 'test_helper' class FactoryTest < Test::Unit::TestCase factory = Factory.new(:post) context "defining a factory" do setup do @name = :user @factory = mock('factory') @factory.stubs(:factory_name).returns(@name) @options = { :class => 'magic' } Factory.stubs(:new).returns(@factory) end should "create a new factory using the specified name and options" do Factory.expects(:new).with(@name, @options).returns(@factory) Factory.define(@name, @options) {|f| } end should "pass the factory do the block" do yielded = nil Factory.define(@name) do |y| yielded = y end assert_equal @factory, yielded end should "add the factory to the list of factories" do Factory.define(@name) {|f| } assert_equal Factory.factories[@name], @factory, "Factories: #{Factory.factories.inspect}" end end context "a factory" do setup do @name = :user @class = User @factory = Factory.new(@name) end should "have a factory name" do assert_equal @name, @factory.factory_name end should "have a build class" do assert_equal @class, @factory.build_class end should "have a default strategy" do assert_equal :create, @factory.default_strategy end should "not allow the same attribute to be added twice" do assert_raise(Factory::AttributeDefinitionError) do 2.times { @factory.add_attribute :first_name } end end should "add a static attribute when an attribute is defined with a value" do attribute = mock('attribute', :name => :name) Factory::Attribute::Static. expects(:new). with(:name, 'value'). returns(attribute) @factory.add_attribute(:name, 'value') end should "add a dynamic attribute when an attribute is defined with a block" do attribute = mock('attribute', :name => :name) block = lambda {} Factory::Attribute::Dynamic. expects(:new). with(:name, block). returns(attribute) @factory.add_attribute(:name, &block) end should "raise for an attribute with a value and a block" do assert_raise(Factory::AttributeDefinitionError) do @factory.add_attribute(:name, 'value') {} end end context "adding an attribute using a in-line sequence" do should 'create the sequence' do Factory::Sequence. expects(:new) @factory.sequence(:name) {} end should 'add a dynamic attribute' do attr = mock('attribute', :name => :name) Factory::Attribute::Dynamic. expects(:new). with(:name, instance_of(Proc)). returns(attr) @factory.sequence(:name) {} assert @factory.attributes.include?(attr) end end context "after adding an attribute" do setup do @attribute = mock('attribute') @proxy = mock('proxy') @attribute. stubs(:name). returns(:name) @attribute. stubs(:add_to) @proxy. stubs(:set) @proxy. stubs(:result).returns('result') Factory::Attribute::Static.stubs(:new). returns(@attribute) Factory::Proxy::Build. stubs(:new). returns(@proxy) @factory.add_attribute(:name, 'value') end should "create the right proxy using the build class when running" do Factory::Proxy::Build. expects(:new). with(@factory.build_class). returns(@proxy) @factory.run(Factory::Proxy::Build, {}) end should "add the attribute to the proxy when running" do @attribute.expects(:add_to).with(@proxy) @factory.run(Factory::Proxy::Build, {}) end should "return the result from the proxy when running" do @proxy.expects(:result).with().returns('result') assert_equal 'result', @factory.run(Factory::Proxy::Build, {}) end end should "add an association without a factory name or overrides" do factory = Factory.new(:post) name = :user attr = 'attribute' Factory::Attribute::Association. expects(:new). with(name, name, {}). returns(attr) factory.association(name) assert factory.attributes.include?(attr) end should "add an association with overrides" do factory = Factory.new(:post) name = :user attr = 'attribute' overrides = { :first_name => 'Ben' } Factory::Attribute::Association. expects(:new). with(name, name, overrides). returns(attr) factory.association(name, overrides) assert factory.attributes.include?(attr) end should "add an association with a factory name" do factory = Factory.new(:post) attr = 'attribute' Factory::Attribute::Association. expects(:new). with(:author, :user, {}). returns(attr) factory.association(:author, :factory => :user) assert factory.attributes.include?(attr) end should "add an association with a factory name and overrides" do factory = Factory.new(:post) attr = 'attribute' Factory::Attribute::Association. expects(:new). with(:author, :user, :first_name => 'Ben'). returns(attr) factory.association(:author, :factory => :user, :first_name => 'Ben') assert factory.attributes.include?(attr) end should "raise for a self referencing association" do factory = Factory.new(:post) assert_raise(Factory::AssociationDefinitionError) do factory.association(:parent, :factory => :post) end end should "add an attribute using the method name when passed an undefined method" do attr = mock('attribute', :name => :name) block = lambda {} Factory::Attribute::Static. expects(:new). with(:name, 'value'). returns(attr) @factory.send(:name, 'value') assert @factory.attributes.include?(attr) end context "when overriding generated attributes with a hash" do setup do @attr = :name @value = 'The price is right!' @hash = { @attr => @value } end should "return the overridden value in the generated attributes" do @factory.add_attribute(@attr, 'The price is wrong, Bob!') result = @factory.run(Factory::Proxy::AttributesFor, @hash) assert_equal @value, result[@attr] end should "not call a lazy attribute block for an overridden attribute" do @factory.add_attribute(@attr) { flunk } result = @factory.run(Factory::Proxy::AttributesFor, @hash) end should "override a symbol parameter with a string parameter" do @factory.add_attribute(@attr, 'The price is wrong, Bob!') @hash = { @attr.to_s => @value } result = @factory.run(Factory::Proxy::AttributesFor, @hash) assert_equal @value, result[@attr] end end context "overriding an attribute with an alias" do setup do @factory.add_attribute(:test, 'original') Factory.alias(/(.*)_alias/, '\1') @result = @factory.run(Factory::Proxy::AttributesFor, :test_alias => 'new') end should "use the passed in value for the alias" do assert_equal 'new', @result[:test_alias] end should "discard the predefined value for the attribute" do assert_nil @result[:test] end 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 "when defined with a class instead of a name" do setup do @class = ArgumentError @name = :argument_error @factory = Factory.new(@class) end should "guess the name from the class" do assert_equal @name, @factory.factory_name end should "use the class as the build class" do assert_equal @class, @factory.build_class end end context "when defined with a custom class name" do setup do @class = ArgumentError @factory = Factory.new(:author, :class => :argument_error) end should "use the specified class as the build class" do assert_equal @class, @factory.build_class end end end context "a factory with a name ending in s" do setup do @name = :business @class = Business @factory = Factory.new(@name) end should "have a factory name" do assert_equal @name, @factory.factory_name end should "have a build class" do assert_equal @class, @factory.build_class end end context "a factory with a string for a name" do setup do @name = :user @factory = Factory.new(@name.to_s) {} end should "convert the string to a symbol" do assert_equal @name, @factory.factory_name end end context "a factory defined with a string name" do setup do Factory.factories = {} @name = :user @factory = Factory.define(@name.to_s) {} end should "store the factory using a symbol" do assert_equal @factory, Factory.factories[@name] end end context "after defining a factory" do setup do @name = :user @factory = mock('factory') Factory.factories[@name] = @factory end teardown { Factory.factories.clear } should "use Proxy::AttributesFor for Factory.attributes_for" do @factory. expects(:run). with(Factory::Proxy::AttributesFor, :attr => 'value'). returns('result') assert_equal 'result', Factory.attributes_for(@name, :attr => 'value') end should "use Proxy::Build for Factory.build" do @factory. expects(:run). with(Factory::Proxy::Build, :attr => 'value'). returns('result') assert_equal 'result', Factory.build(@name, :attr => 'value') end should "use Proxy::Create for Factory.create" do @factory. expects(:run). with(Factory::Proxy::Create, :attr => 'value'). returns('result') assert_equal 'result', Factory.create(@name, :attr => 'value') end should "use Proxy::Stub for Factory.stub" do @factory. expects(:run). with(Factory::Proxy::Stub, :attr => 'value'). returns('result') assert_equal 'result', Factory.stub(@name, :attr => 'value') end should "use default strategy option as Factory.default_strategy" do @factory.stubs(:default_strategy).returns(:create) @factory. expects(:run). with(Factory::Proxy::Create, :attr => 'value'). returns('result') assert_equal 'result', Factory.default_strategy(@name, :attr => 'value') end should "use the default strategy for the global Factory method" do @factory.stubs(:default_strategy).returns(:create) @factory. expects(:run). with(Factory::Proxy::Create, :attr => 'value'). returns('result') assert_equal 'result', Factory(@name, :attr => 'value') end [:build, :create, :attributes_for, :stub].each do |method| should "raise an ArgumentError on #{method} with a nonexistant factory" do assert_raise(ArgumentError) { Factory.send(method, :bogus) } end should "recognize either 'name' or :name for Factory.#{method}" do @factory.stubs(:run) assert_nothing_raised { Factory.send(method, @name.to_s) } assert_nothing_raised { Factory.send(method, @name.to_sym) } end end end context 'defining a factory with a parent parameter' do setup do @parent = Factory.define :object do |f| f.name 'Name' end end should 'raise an ArgumentError when trying to use a non-existent factory as parent' do assert_raise(ArgumentError) { Factory.define(:child, :parent => :nonexsitent) {} } end should 'create a new factory using the class of the parent' do child = Factory.define(:child, :parent => :object) {} assert_equal @parent.build_class, child.build_class end should 'create a new factory with attributes of the parent' do child = Factory.define(:child, :parent => :object) {} assert_equal 1, child.attributes.size assert_equal :name, child.attributes.first.name end should 'allow to define additional attributes' do child = Factory.define(:child, :parent => :object) do |f| f.email 'person@somebody.com' end assert_equal 2, child.attributes.size end should 'allow to override parent attributes' do child = Factory.define(:child, :parent => :object) do |f| f.name { 'Child Name' } end assert_equal 1, child.attributes.size assert_kind_of Factory::Attribute::Dynamic, child.attributes.first end end context 'defining a factory with a default strategy parameter' do should 'raise an ArgumentError when trying to use a non-existent factory' do assert_raise(ArgumentError) { Factory.define(:object, :default_strategy => :nonexistent) {} } end should 'create a new factory with a specified default strategy' do factory = Factory.define(:object, :default_strategy => :stub) {} assert_equal :stub, factory.default_strategy end end def self.context_in_directory_with_files(*files) context "in a directory with #{files.to_sentence}" do setup do @pwd = Dir.pwd @tmp_dir = File.join(File.dirname(__FILE__), 'tmp') FileUtils.mkdir_p @tmp_dir Dir.chdir(@tmp_dir) files.each do |file| FileUtils.mkdir_p File.dirname(file) FileUtils.touch file Factory.stubs(:require).with(file) end end teardown do Dir.chdir(@pwd) FileUtils.rm_rf(@tmp_dir) end yield end end def self.should_require_definitions_from(file) should "load definitions from #{file}" do Factory.expects(:require).with(file) Factory.find_definitions end end context_in_directory_with_files 'factories.rb' do should_require_definitions_from 'factories.rb' end %w(spec test).each do |dir| context_in_directory_with_files File.join(dir, 'factories.rb') do should_require_definitions_from "#{dir}/factories.rb" end context_in_directory_with_files File.join(dir, 'factories', 'post_factory.rb') do should_require_definitions_from "#{dir}/factories/post_factory.rb" end context_in_directory_with_files File.join(dir, 'factories', 'post_factory.rb'), File.join(dir, 'factories', 'person_factory.rb') do should_require_definitions_from "#{dir}/factories/post_factory.rb" should_require_definitions_from "#{dir}/factories/person_factory.rb" end context_in_directory_with_files File.join(dir, 'factories.rb'), File.join(dir, 'factories', 'post_factory.rb'), File.join(dir, 'factories', 'person_factory.rb') do should_require_definitions_from "#{dir}/factories.rb" should_require_definitions_from "#{dir}/factories/post_factory.rb" should_require_definitions_from "#{dir}/factories/person_factory.rb" end end end