thoughtbot-factory_girl 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +4 -5
  2. data/README.rdoc +6 -6
  3. data/Rakefile +11 -14
  4. data/lib/factory_girl/attribute.rb +3 -2
  5. data/lib/factory_girl/attribute/dynamic.rb +5 -2
  6. data/lib/factory_girl/proxy/stub.rb +33 -12
  7. data/lib/factory_girl/sequence.rb +3 -0
  8. data/spec/factory_girl/aliases_spec.rb +29 -0
  9. data/spec/factory_girl/attribute/association_spec.rb +25 -0
  10. data/spec/factory_girl/attribute/dynamic_spec.rb +49 -0
  11. data/spec/factory_girl/attribute/static_spec.rb +29 -0
  12. data/spec/factory_girl/attribute_spec.rb +30 -0
  13. data/spec/factory_girl/factory_spec.rb +490 -0
  14. data/spec/factory_girl/proxy/attributes_for_spec.rb +52 -0
  15. data/spec/factory_girl/proxy/build_spec.rb +73 -0
  16. data/spec/factory_girl/proxy/create_spec.rb +83 -0
  17. data/spec/factory_girl/proxy/stub_spec.rb +69 -0
  18. data/spec/factory_girl/proxy_spec.rb +28 -0
  19. data/spec/factory_girl/sequence_spec.rb +66 -0
  20. data/spec/factory_girl/syntax/blueprint_spec.rb +34 -0
  21. data/spec/factory_girl/syntax/generate_spec.rb +57 -0
  22. data/spec/factory_girl/syntax/make_spec.rb +35 -0
  23. data/spec/factory_girl/syntax/sham_spec.rb +35 -0
  24. data/spec/integration_spec.rb +265 -0
  25. data/{test → spec}/models.rb +0 -0
  26. data/{test/test_helper.rb → spec/spec_helper.rb} +12 -5
  27. metadata +42 -43
  28. data/test/aliases_test.rb +0 -29
  29. data/test/association_attribute_test.rb +0 -31
  30. data/test/attribute_test.rb +0 -32
  31. data/test/attributes_for_strategy_test.rb +0 -55
  32. data/test/build_strategy_test.rb +0 -79
  33. data/test/create_strategy_test.rb +0 -90
  34. data/test/dynamic_attribute_test.rb +0 -41
  35. data/test/factory_test.rb +0 -513
  36. data/test/integration_test.rb +0 -235
  37. data/test/sequence_test.rb +0 -76
  38. data/test/static_attribute_test.rb +0 -33
  39. data/test/strategy_test.rb +0 -33
  40. data/test/stub_strategy_test.rb +0 -61
  41. data/test/syntax/blueprint_test.rb +0 -39
  42. data/test/syntax/generate_test.rb +0 -63
  43. data/test/syntax/make_test.rb +0 -39
  44. data/test/syntax/sham_test.rb +0 -40
@@ -1,10 +1,9 @@
1
- We're using GitHub[http://github.com/thoughtbot/factory_girl/tree/master] and Lighthouse[http://thoughtbot.lighthouseapp.com/projects/14354], and we've been getting any combination of github pull requests, Lighthouse tickets, patches, emails, etc. We need to normalize this workflow to make sure we don't miss any fixes.
1
+ We're using GitHub[http://github.com/thoughtbot/factory_girl/tree/master], and we've been getting any combination of github pull requests, patches, emails, etc. We need to normalize this workflow to make sure we don't miss any fixes.
2
2
 
3
3
  * Make sure you're accessing the source from the {official repository}[http://github.com/thoughtbot/factory_girl/tree/master].
4
- * We prefer git branches over patches, but we can take either.
5
- * If you're using git, please make a branch for each separate contribution. We can cherry pick your commits, but pulling from a branch is easier.
4
+ * Please make a branch for each separate contribution. We can cherry pick your commits, but pulling from a branch is easier.
6
5
  * If you're submitting patches, please cut each fix or feature into a separate patch.
7
- * There should be a Lighthouse[http://thoughtbot.lighthouseapp.com/projects/14354] ticket for any submission. If you've found a bug and want to fix it, open a new ticket at the same time.
8
- * Please <b>don't send pull requests</b> Just update the lighthouse ticket with the url for your fix (or attach the patch) when it's ready. The github pull requests pretty much get dropped on the floor until someone with commit rights notices them in the mailbox.
6
+ * There should be an issue[http://github.com/thoughtbot/factory_girl/issues] for any submission. If you've found a bug and want to fix it, open a new ticket at the same time.
7
+ * Please <b>don't send pull requests</b> Just update the issue with the url for your fix when it's ready. The github pull requests pretty much get dropped on the floor until someone with commit rights notices them in the mailbox.
9
8
  * Contributions without tests won't be accepted.
10
9
  * Please don't submit patches or branches that update the Gem version.
@@ -1,6 +1,6 @@
1
1
  = factory_girl
2
2
 
3
- factory_girl is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritence.
3
+ factory_girl is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.
4
4
 
5
5
  == Download
6
6
 
@@ -18,7 +18,7 @@ in your environment.rb if you want to use Rails 2.1+'s dependency manager:
18
18
 
19
19
  == Defining factories
20
20
 
21
- Each factory has a name and a set of attributes. The name is used to guess the class of the object by default, but it's possible to excplicitly specify it:
21
+ Each factory has a name and a set of attributes. The name is used to guess the class of the object by default, but it's possible to explicitly specify it:
22
22
 
23
23
  # This will guess the User class
24
24
  Factory.define :user do |u|
@@ -41,7 +41,7 @@ Each factory has a name and a set of attributes. The name is used to guess the c
41
41
  u.admin true
42
42
  end
43
43
 
44
- It is highly recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritence to cover common scenarios for each class.
44
+ It is highly recommended that you have one factory for each class that provides the simplest set of attributes necessary to create an instance of that class. If you're creating ActiveRecord objects, that means that you should only provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class.
45
45
 
46
46
  Factories can either be defined anywhere, but will automatically be loaded if they are defined in files at the following locations:
47
47
 
@@ -148,7 +148,7 @@ be left out.
148
148
 
149
149
  == Inheritance
150
150
 
151
- You can easily create multiple factories for the same class without repeating common attributes by using inheritence:
151
+ You can easily create multiple factories for the same class without repeating common attributes by using inheritance:
152
152
 
153
153
  Factory.define :post do |p|
154
154
  # the 'title' attribute is required for all posts
@@ -199,11 +199,11 @@ Users' tastes for syntax vary dramatically, but most users are looking for a com
199
199
 
200
200
  Our blog: http://giantrobots.thoughtbot.com
201
201
 
202
- factory_girl rdoc: http://dev.thoughtbot.com/factory_girl
202
+ factory_girl rdoc: http://rdoc.info/projects/thoughtbot/factory_girl
203
203
 
204
204
  Mailing list: http://groups.google.com/group/factory_girl
205
205
 
206
- factory_girl tickets: http://thoughtbot.lighthouseapp.com/projects/14354-factory_girl
206
+ factory_girl issues: http://github.com/thoughtbot/factory_girl/issues
207
207
 
208
208
  == Contributing
209
209
 
data/Rakefile CHANGED
@@ -1,26 +1,22 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
- require 'rake/testtask'
4
3
  require 'rake/rdoctask'
5
4
  require 'rake/gempackagetask'
6
5
  require 'rcov/rcovtask'
7
6
  require 'date'
8
7
 
9
- desc 'Default: run unit tests.'
10
- task :default => :test
8
+ require 'spec/rake/spectask'
11
9
 
12
- desc 'Test the factory_girl plugin.'
13
- Rake::TestTask.new(:test) do |t|
14
- t.libs << 'lib'
15
- t.libs << 'test'
16
- t.pattern = 'test/**/*_test.rb'
17
- t.verbose = true
10
+ desc 'Default: run the specs.'
11
+ task :default => :spec
12
+
13
+ Spec::Rake::SpecTask.new do |t|
14
+ t.spec_opts = ['--options', "spec/spec.opts"]
18
15
  end
19
16
 
20
17
  desc 'Performs code coverage on the factory_girl plugin.'
21
18
  Rcov::RcovTask.new do |t|
22
- t.libs << "test"
23
- t.test_files = FileList['test/*_test.rb']
19
+ t.test_files = FileList['spec/*_spec.rb']
24
20
  t.verbose = true
25
21
  end
26
22
 
@@ -41,16 +37,16 @@ end
41
37
 
42
38
  spec = Gem::Specification.new do |s|
43
39
  s.name = %q{factory_girl}
44
- s.version = "1.2.0"
40
+ s.version = "1.2.2"
45
41
  s.summary = %q{factory_girl provides a framework and DSL for defining and
46
42
  using model instance factories.}
47
43
  s.description = %q{factory_girl provides a framework and DSL for defining and
48
44
  using factories - less error-prone, more explicit, and
49
45
  all-around easier to work with than fixtures.}
50
46
 
51
- s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'test/**/*.rb']
47
+ s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'spec/**/*.rb']
52
48
  s.require_path = 'lib'
53
- s.test_files = Dir[*['test/**/*_test.rb']]
49
+ s.test_files = Dir[*['spec/**/*_spec.rb']]
54
50
 
55
51
  s.has_rdoc = true
56
52
  s.extra_rdoc_files = ["README.rdoc"]
@@ -58,6 +54,7 @@ spec = Gem::Specification.new do |s|
58
54
 
59
55
  s.authors = ["Joe Ferris"]
60
56
  s.email = %q{jferris@thoughtbot.com}
57
+ s.homepage = "http://thoughtbot.com/projects/factory_girl"
61
58
 
62
59
  s.platform = Gem::Platform::RUBY
63
60
  end
@@ -15,9 +15,10 @@ class Factory
15
15
  @name = name.to_sym
16
16
 
17
17
  if @name.to_s =~ /=$/
18
+ attribute_name = $`
18
19
  raise AttributeDefinitionError,
19
- "factory_girl uses 'f.#{@name.to_s.chop} value' syntax " +
20
- "rather than 'f.#{@name} = value'"
20
+ "factory_girl uses 'f.#{attribute_name} value' syntax " +
21
+ "rather than 'f.#{attribute_name} = value'"
21
22
  end
22
23
  end
23
24
 
@@ -2,14 +2,17 @@ class Factory
2
2
  class Attribute #:nodoc:
3
3
 
4
4
  class Dynamic < Attribute #:nodoc:
5
-
6
5
  def initialize(name, block)
7
6
  super(name)
8
7
  @block = block
9
8
  end
10
9
 
11
10
  def add_to(proxy)
12
- proxy.set(name, @block.call(proxy))
11
+ value = @block.arity.zero? ? @block.call : @block.call(proxy)
12
+ if Factory::Sequence === value
13
+ raise SequenceAbuseError
14
+ end
15
+ proxy.set(name, value)
13
16
  end
14
17
  end
15
18
 
@@ -1,27 +1,48 @@
1
1
  class Factory
2
2
  class Proxy
3
3
  class Stub < Proxy #:nodoc:
4
+ @@next_id = 1000
5
+
4
6
  def initialize(klass)
5
- @mock = Object.new
7
+ @stub = klass.new
8
+ @stub.id = next_id
9
+ @stub.instance_eval do
10
+ def new_record?
11
+ id.nil?
12
+ end
13
+
14
+ def connection
15
+ raise "stubbed models are not allowed to access the database"
16
+ end
17
+
18
+ def reload
19
+ raise "stubbed models are not allowed to access the database"
20
+ end
21
+ end
22
+ end
23
+
24
+ def next_id
25
+ @@next_id += 1
6
26
  end
7
-
27
+
8
28
  def get(attribute)
9
- @mock.send(attribute)
29
+ @stub.send(attribute)
10
30
  end
11
-
31
+
12
32
  def set(attribute, value)
13
- unless @mock.respond_to?("#{attribute}=")
14
- class << @mock; self end.send(:attr_accessor, attribute)
15
- end
16
- @mock.send("#{attribute}=", value)
33
+ @stub.send(:"#{attribute}=", value)
17
34
  end
18
-
35
+
19
36
  def associate(name, factory, attributes)
20
- set(name, nil)
37
+ set(name, Factory.stub(factory, attributes))
38
+ end
39
+
40
+ def association(factory, overrides = {})
41
+ Factory.stub(factory, overrides)
21
42
  end
22
-
43
+
23
44
  def result
24
- @mock
45
+ @stub
25
46
  end
26
47
  end
27
48
  end
@@ -1,5 +1,8 @@
1
1
  class Factory
2
2
 
3
+ # Raised when calling Factory.sequence from a dynamic attribute block
4
+ class SequenceAbuseError < StandardError; end
5
+
3
6
  # Sequences are defined using Factory.sequence. Sequence values are generated
4
7
  # using next.
5
8
  class Sequence
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe Factory, "aliases" do
4
+
5
+ it "should include an attribute as an alias for itself by default" do
6
+ Factory.aliases_for(:test).should include(:test)
7
+ end
8
+
9
+ it "should include the root of a foreign key as an alias by default" do
10
+ Factory.aliases_for(:test_id).should include(:test)
11
+ end
12
+
13
+ it "should include an attribute's foreign key as an alias by default" do
14
+ Factory.aliases_for(:test).should include(:test_id)
15
+ end
16
+
17
+ describe "after adding an alias" do
18
+
19
+ before do
20
+ Factory.alias(/(.*)_suffix/, '\1')
21
+ end
22
+
23
+ it "should return the alias in the aliases list" do
24
+ Factory.aliases_for(:test_suffix).should include(:test)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe Factory::Attribute::Association do
4
+ before do
5
+ @name = :author
6
+ @factory = :user
7
+ @overrides = { :first_name => 'John' }
8
+ @attr = Factory::Attribute::Association.new(@name, @factory, @overrides)
9
+ end
10
+
11
+ it "should have a name" do
12
+ @attr.name.should == @name
13
+ end
14
+
15
+ it "should tell the proxy to associate when being added to a proxy" do
16
+ proxy = "proxy"
17
+ stub(proxy).associate
18
+ @attr.add_to(proxy)
19
+ proxy.should have_received.associate(@name, @factory, @overrides)
20
+ end
21
+
22
+ it "should convert names to symbols" do
23
+ Factory::Attribute::Association.new('name', :user, {}).name.should == :name
24
+ end
25
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe Factory::Attribute::Dynamic do
4
+ before do
5
+ @name = :first_name
6
+ @block = lambda { 'value' }
7
+ @attr = Factory::Attribute::Dynamic.new(@name, @block)
8
+ end
9
+
10
+ it "should have a name" do
11
+ @attr.name.should == @name
12
+ end
13
+
14
+ it "should call the block to set a value" do
15
+ @proxy = "proxy"
16
+ stub(@proxy).set
17
+ @attr.add_to(@proxy)
18
+ @proxy.should have_received.set(@name, 'value')
19
+ end
20
+
21
+ it "should yield the proxy to the block when adding its value to a proxy" do
22
+ @block = lambda {|a| a }
23
+ @attr = Factory::Attribute::Dynamic.new(:user, @block)
24
+ @proxy = "proxy"
25
+ stub(@proxy).set
26
+ @attr.add_to(@proxy)
27
+ @proxy.should have_received.set(:user, @proxy)
28
+ end
29
+
30
+ it "should raise an error when defining an attribute writer" do
31
+ lambda {
32
+ Factory::Attribute::Dynamic.new('test=', nil)
33
+ }.should raise_error(Factory::AttributeDefinitionError)
34
+ end
35
+
36
+ it "should raise an error when returning a sequence" do
37
+ stub(Factory).sequence { Factory::Sequence.new }
38
+ block = lambda { Factory.sequence(:email) }
39
+ attr = Factory::Attribute::Dynamic.new(:email, block)
40
+ proxy = stub!.set.subject
41
+ lambda {
42
+ attr.add_to(proxy)
43
+ }.should raise_error(Factory::SequenceAbuseError)
44
+ end
45
+
46
+ it "should convert names to symbols" do
47
+ Factory::Attribute::Dynamic.new('name', nil).name.should == :name
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ describe Factory::Attribute::Static do
4
+ before do
5
+ @name = :first_name
6
+ @value = 'John'
7
+ @attr = Factory::Attribute::Static.new(@name, @value)
8
+ end
9
+
10
+ it "should have a name" do
11
+ @attr.name.should == @name
12
+ end
13
+
14
+ it "should set its static value on a proxy" do
15
+ @proxy = "proxy"
16
+ mock(@proxy).set(@name, @value)
17
+ @attr.add_to(@proxy)
18
+ end
19
+
20
+ it "should raise an error when defining an attribute writer" do
21
+ lambda {
22
+ Factory::Attribute::Static.new('test=', nil)
23
+ }.should raise_error(Factory::AttributeDefinitionError)
24
+ end
25
+
26
+ it "should convert names to symbols" do
27
+ Factory::Attribute::Static.new('name', nil).name.should == :name
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe Factory::Attribute do
4
+ before do
5
+ @name = :user
6
+ @attr = Factory::Attribute.new(@name)
7
+ end
8
+
9
+ it "should have a name" do
10
+ @attr.name.should == @name
11
+ end
12
+
13
+ it "should do nothing when being added to a proxy" do
14
+ @proxy = "proxy"
15
+ stub(@proxy).set
16
+ @attr.add_to(@proxy)
17
+ @proxy.should have_received.set.never
18
+ end
19
+
20
+ it "should raise an error when defining an attribute writer" do
21
+ error_message = %{factory_girl uses 'f.test value' syntax rather than 'f.test = value'}
22
+ lambda {
23
+ Factory::Attribute.new('test=')
24
+ }.should raise_error(Factory::AttributeDefinitionError, error_message)
25
+ end
26
+
27
+ it "should convert names to symbols" do
28
+ Factory::Attribute.new('name').name.should == :name
29
+ end
30
+ end
@@ -0,0 +1,490 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ describe Factory do
4
+ describe "defining a factory" do
5
+ before do
6
+ @name = :user
7
+ @factory = "factory"
8
+ stub(@factory).factory_name { @name }
9
+ @options = { :class => 'magic' }
10
+ stub(Factory).new { @factory }
11
+ end
12
+
13
+ it "should create a new factory using the specified name and options" do
14
+ mock(Factory).new(@name, @options) { @factory }
15
+ Factory.define(@name, @options) {|f| }
16
+ end
17
+
18
+ it "should pass the factory do the block" do
19
+ yielded = nil
20
+ Factory.define(@name) do |y|
21
+ yielded = y
22
+ end
23
+ yielded.should == @factory
24
+ end
25
+
26
+ it "should add the factory to the list of factories" do
27
+ Factory.define(@name) {|f| }
28
+ @factory.should == Factory.factories[@name]
29
+ end
30
+ end
31
+
32
+ describe "a factory" do
33
+ before do
34
+ @name = :user
35
+ @class = User
36
+ @factory = Factory.new(@name)
37
+ end
38
+
39
+ it "should have a factory name" do
40
+ @factory.factory_name.should == @name
41
+ end
42
+
43
+ it "should have a build class" do
44
+ @factory.build_class.should == @class
45
+ end
46
+
47
+ it "should have a default strategy" do
48
+ @factory.default_strategy.should == :create
49
+ end
50
+
51
+ it "should not allow the same attribute to be added twice" do
52
+ lambda {
53
+ 2.times { @factory.add_attribute :first_name }
54
+ }.should raise_error(Factory::AttributeDefinitionError)
55
+ end
56
+
57
+ it "should add a static attribute when an attribute is defined with a value" do
58
+ attribute = 'attribute'
59
+ stub(attribute).name { :name }
60
+ mock(Factory::Attribute::Static).new(:name, 'value') { attribute }
61
+ @factory.add_attribute(:name, 'value')
62
+ end
63
+
64
+ it "should add a dynamic attribute when an attribute is defined with a block" do
65
+ attribute = 'attribute'
66
+ stub(attribute).name { :name }
67
+ block = lambda {}
68
+ mock(Factory::Attribute::Dynamic).new(:name, block) { attribute }
69
+ @factory.add_attribute(:name, &block)
70
+ end
71
+
72
+ it "should raise for an attribute with a value and a block" do
73
+ lambda {
74
+ @factory.add_attribute(:name, 'value') {}
75
+ }.should raise_error(Factory::AttributeDefinitionError)
76
+ end
77
+
78
+ describe "adding an attribute using a in-line sequence" do
79
+ it "should create the sequence" do
80
+ mock(Factory::Sequence).new
81
+ @factory.sequence(:name) {}
82
+ end
83
+
84
+ it "should add a dynamic attribute" do
85
+ attribute = 'attribute'
86
+ stub(attribute).name { :name }
87
+ mock(Factory::Attribute::Dynamic).new(:name, is_a(Proc)) { attribute }
88
+ @factory.sequence(:name) {}
89
+ @factory.attributes.should include(attribute)
90
+ end
91
+ end
92
+
93
+ describe "after adding an attribute" do
94
+ before do
95
+ @attribute = "attribute"
96
+ @proxy = "proxy"
97
+
98
+ stub(@attribute).name { :name }
99
+ stub(@attribute).add_to
100
+ stub(@proxy).set
101
+ stub(@proxy).result { 'result' }
102
+ stub(Factory::Attribute::Static).new { @attribute }
103
+ stub(Factory::Proxy::Build).new { @proxy }
104
+
105
+ @factory.add_attribute(:name, 'value')
106
+ end
107
+
108
+ it "should create the right proxy using the build class when running" do
109
+ mock(Factory::Proxy::Build).new(@factory.build_class) { @proxy }
110
+ @factory.run(Factory::Proxy::Build, {})
111
+ end
112
+
113
+ it "should add the attribute to the proxy when running" do
114
+ mock(@attribute).add_to(@proxy)
115
+ @factory.run(Factory::Proxy::Build, {})
116
+ end
117
+
118
+ it "should return the result from the proxy when running" do
119
+ mock(@proxy).result() { 'result' }
120
+ @factory.run(Factory::Proxy::Build, {}).should == 'result'
121
+ end
122
+ end
123
+
124
+ it "should add an association without a factory name or overrides" do
125
+ factory = Factory.new(:post)
126
+ name = :user
127
+ attr = 'attribute'
128
+ mock(Factory::Attribute::Association).new(name, name, {}) { attr }
129
+ factory.association(name)
130
+ factory.attributes.should include(attr)
131
+ end
132
+
133
+ it "should add an association with overrides" do
134
+ factory = Factory.new(:post)
135
+ name = :user
136
+ attr = 'attribute'
137
+ overrides = { :first_name => 'Ben' }
138
+ mock(Factory::Attribute::Association).new(name, name, overrides) { attr }
139
+ factory.association(name, overrides)
140
+ factory.attributes.should include(attr)
141
+ end
142
+
143
+ it "should add an association with a factory name" do
144
+ factory = Factory.new(:post)
145
+ attr = 'attribute'
146
+ mock(Factory::Attribute::Association).new(:author, :user, {}) { attr }
147
+ factory.association(:author, :factory => :user)
148
+ factory.attributes.should include(attr)
149
+ end
150
+
151
+ it "should add an association with a factory name and overrides" do
152
+ factory = Factory.new(:post)
153
+ attr = 'attribute'
154
+ mock(Factory::Attribute::Association).new(:author, :user, :first_name => 'Ben') { attr }
155
+ factory.association(:author, :factory => :user, :first_name => 'Ben')
156
+ factory.attributes.should include(attr)
157
+ end
158
+
159
+ it "should raise for a self referencing association" do
160
+ factory = Factory.new(:post)
161
+ lambda {
162
+ factory.association(:parent, :factory => :post)
163
+ }.should raise_error(Factory::AssociationDefinitionError)
164
+ end
165
+
166
+ it "should add an attribute using the method name when passed an undefined method" do
167
+ attribute = 'attribute'
168
+ stub(attribute).name { :name }
169
+ block = lambda {}
170
+ mock(Factory::Attribute::Static).new(:name, 'value') { attribute }
171
+ @factory.send(:name, 'value')
172
+ @factory.attributes.should include(attribute)
173
+ end
174
+
175
+ describe "when overriding generated attributes with a hash" do
176
+ before do
177
+ @attr = :name
178
+ @value = 'The price is right!'
179
+ @hash = { @attr => @value }
180
+ end
181
+
182
+ it "should return the overridden value in the generated attributes" do
183
+ @factory.add_attribute(@attr, 'The price is wrong, Bob!')
184
+ result = @factory.run(Factory::Proxy::AttributesFor, @hash)
185
+ result[@attr].should == @value
186
+ end
187
+
188
+ it "should not call a lazy attribute block for an overridden attribute" do
189
+ @factory.add_attribute(@attr) { flunk }
190
+ result = @factory.run(Factory::Proxy::AttributesFor, @hash)
191
+ end
192
+
193
+ it "should override a symbol parameter with a string parameter" do
194
+ @factory.add_attribute(@attr, 'The price is wrong, Bob!')
195
+ @hash = { @attr.to_s => @value }
196
+ result = @factory.run(Factory::Proxy::AttributesFor, @hash)
197
+ result[@attr].should == @value
198
+ end
199
+ end
200
+
201
+ describe "overriding an attribute with an alias" do
202
+ before do
203
+ @factory.add_attribute(:test, 'original')
204
+ Factory.alias(/(.*)_alias/, '\1')
205
+ @result = @factory.run(Factory::Proxy::AttributesFor,
206
+ :test_alias => 'new')
207
+ end
208
+
209
+ it "should use the passed in value for the alias" do
210
+ @result[:test_alias].should == 'new'
211
+ end
212
+
213
+ it "should discard the predefined value for the attribute" do
214
+ @result[:test].should be_nil
215
+ end
216
+ end
217
+
218
+ it "should guess the build class from the factory name" do
219
+ @factory.build_class.should == User
220
+ end
221
+
222
+ describe "when defined with a custom class" do
223
+ before do
224
+ @class = User
225
+ @factory = Factory.new(:author, :class => @class)
226
+ end
227
+
228
+ it "should use the specified class as the build class" do
229
+ @factory.build_class.should == @class
230
+ end
231
+ end
232
+
233
+ describe "when defined with a class instead of a name" do
234
+ before do
235
+ @class = ArgumentError
236
+ @name = :argument_error
237
+ @factory = Factory.new(@class)
238
+ end
239
+
240
+ it "should guess the name from the class" do
241
+ @factory.factory_name.should == @name
242
+ end
243
+
244
+ it "should use the class as the build class" do
245
+ @factory.build_class.should == @class
246
+ end
247
+ end
248
+
249
+ describe "when defined with a custom class name" do
250
+ before do
251
+ @class = ArgumentError
252
+ @factory = Factory.new(:author, :class => :argument_error)
253
+ end
254
+
255
+ it "should use the specified class as the build class" do
256
+ @factory.build_class.should == @class
257
+ end
258
+ end
259
+ end
260
+
261
+ describe "a factory with a name ending in s" do
262
+ before do
263
+ @name = :business
264
+ @class = Business
265
+ @factory = Factory.new(@name)
266
+ end
267
+
268
+ it "should have a factory name" do
269
+ @factory.factory_name.should == @name
270
+ end
271
+
272
+ it "should have a build class" do
273
+ @factory.build_class.should == @class
274
+ end
275
+ end
276
+
277
+ describe "a factory with a string for a name" do
278
+ before do
279
+ @name = :user
280
+ @factory = Factory.new(@name.to_s) {}
281
+ end
282
+
283
+ it "should convert the string to a symbol" do
284
+ @factory.factory_name.should == @name
285
+ end
286
+ end
287
+
288
+ describe "a factory defined with a string name" do
289
+ before do
290
+ Factory.factories = {}
291
+ @name = :user
292
+ @factory = Factory.define(@name.to_s) {}
293
+ end
294
+
295
+ it "should store the factory using a symbol" do
296
+ Factory.factories[@name].should == @factory
297
+ end
298
+ end
299
+
300
+ describe "after defining a factory" do
301
+ before do
302
+ @name = :user
303
+ @factory = "factory"
304
+
305
+ Factory.factories[@name] = @factory
306
+ end
307
+
308
+ after { Factory.factories.clear }
309
+
310
+ it "should use Proxy::AttributesFor for Factory.attributes_for" do
311
+ mock(@factory).run(Factory::Proxy::AttributesFor, :attr => 'value') { 'result' }
312
+ Factory.attributes_for(@name, :attr => 'value').should == 'result'
313
+ end
314
+
315
+ it "should use Proxy::Build for Factory.build" do
316
+ mock(@factory).run(Factory::Proxy::Build, :attr => 'value') { 'result' }
317
+ Factory.build(@name, :attr => 'value').should == 'result'
318
+ end
319
+
320
+ it "should use Proxy::Create for Factory.create" do
321
+ mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
322
+ Factory.create(@name, :attr => 'value').should == 'result'
323
+ end
324
+
325
+ it "should use Proxy::Stub for Factory.stub" do
326
+ mock(@factory).run(Factory::Proxy::Stub, :attr => 'value') { 'result' }
327
+ Factory.stub(@name, :attr => 'value').should == 'result'
328
+ end
329
+
330
+ it "should use default strategy option as Factory.default_strategy" do
331
+ stub(@factory).default_strategy { :create }
332
+ mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
333
+ Factory.default_strategy(@name, :attr => 'value').should == 'result'
334
+ end
335
+
336
+ it "should use the default strategy for the global Factory method" do
337
+ stub(@factory).default_strategy { :create }
338
+ mock(@factory).run(Factory::Proxy::Create, :attr => 'value') { 'result' }
339
+ Factory(@name, :attr => 'value').should == 'result'
340
+ end
341
+
342
+ [:build, :create, :attributes_for, :stub].each do |method|
343
+ it "should raise an ArgumentError on #{method} with a nonexistant factory" do
344
+ lambda { Factory.send(method, :bogus) }.should raise_error(ArgumentError)
345
+ end
346
+
347
+ it "should recognize either 'name' or :name for Factory.#{method}" do
348
+ stub(@factory).run
349
+ lambda { Factory.send(method, @name.to_s) }.should_not raise_error
350
+ lambda { Factory.send(method, @name.to_sym) }.should_not raise_error
351
+ end
352
+ end
353
+ end
354
+
355
+ describe 'defining a factory with a parent parameter' do
356
+ before do
357
+ @parent = Factory.define :object do |f|
358
+ f.name 'Name'
359
+ end
360
+ end
361
+
362
+ it "should raise an ArgumentError when trying to use a non-existent factory as parent" do
363
+ lambda {
364
+ Factory.define(:child, :parent => :nonexsitent) {}
365
+ }.should raise_error(ArgumentError)
366
+ end
367
+
368
+ it "should create a new factory using the class of the parent" do
369
+ child = Factory.define(:child, :parent => :object) {}
370
+ child.build_class.should == @parent.build_class
371
+ end
372
+
373
+ it "should create a new factory while overriding the parent class" do
374
+ class Other; end
375
+
376
+ child = Factory.define(:child, :parent => :object, :class => Other) {}
377
+ child.build_class.should == Other
378
+ end
379
+
380
+ it "should create a new factory with attributes of the parent" do
381
+ child = Factory.define(:child, :parent => :object) {}
382
+ child.attributes.size.should == 1
383
+ child.attributes.first.name.should == :name
384
+ end
385
+
386
+ it "should allow to define additional attributes" do
387
+ child = Factory.define(:child, :parent => :object) do |f|
388
+ f.email 'person@somebody.com'
389
+ end
390
+ child.attributes.size.should == 2
391
+ end
392
+
393
+ it "should allow to override parent attributes" do
394
+ child = Factory.define(:child, :parent => :object) do |f|
395
+ f.name { 'Child Name' }
396
+ end
397
+ child.attributes.size.should == 1
398
+ child.attributes.first.should be_kind_of(Factory::Attribute::Dynamic)
399
+ end
400
+ end
401
+
402
+ describe 'defining a factory with a default strategy parameter' do
403
+ it "should raise an ArgumentError when trying to use a non-existent factory" do
404
+ lambda {
405
+ Factory.define(:object, :default_strategy => :nonexistent) {}
406
+ }.should raise_error(ArgumentError)
407
+ end
408
+
409
+ it "should create a new factory with a specified default strategy" do
410
+ factory = Factory.define(:object, :default_strategy => :stub) {}
411
+ factory.default_strategy.should == :stub
412
+ end
413
+ end
414
+
415
+ def self.in_directory_with_files(*files)
416
+ before do
417
+ @pwd = Dir.pwd
418
+ @tmp_dir = File.join(File.dirname(__FILE__), 'tmp')
419
+ FileUtils.mkdir_p @tmp_dir
420
+ Dir.chdir(@tmp_dir)
421
+
422
+ files.each do |file|
423
+ FileUtils.mkdir_p File.dirname(file)
424
+ FileUtils.touch file
425
+ stub(Factory).require(file)
426
+ end
427
+ end
428
+
429
+ after do
430
+ Dir.chdir(@pwd)
431
+ FileUtils.rm_rf(@tmp_dir)
432
+ end
433
+ end
434
+
435
+ def require_definitions_from(file)
436
+ simple_matcher do |given, matcher|
437
+ has_received = have_received.require(file)
438
+ result = has_received.matches?(given)
439
+ matcher.description = "require definitions from #{file}"
440
+ matcher.failure_message = has_received.failure_message
441
+ result
442
+ end
443
+ end
444
+
445
+ share_examples_for "finds definitions" do
446
+ before do
447
+ stub(Factory).require
448
+ Factory.find_definitions
449
+ end
450
+ subject { Factory }
451
+ end
452
+
453
+ describe "with factories.rb" do
454
+ in_directory_with_files 'factories.rb'
455
+ it_should_behave_like "finds definitions"
456
+ it { should require_definitions_from('factories.rb') }
457
+ end
458
+
459
+ %w(spec test).each do |dir|
460
+ describe "with a factories file under #{dir}" do
461
+ in_directory_with_files File.join(dir, 'factories.rb')
462
+ it_should_behave_like "finds definitions"
463
+ it { should require_definitions_from("#{dir}/factories.rb") }
464
+ end
465
+
466
+ describe "with a factories file under #{dir}/factories" do
467
+ in_directory_with_files File.join(dir, 'factories', 'post_factory.rb')
468
+ it_should_behave_like "finds definitions"
469
+ it { should require_definitions_from("#{dir}/factories/post_factory.rb") }
470
+ end
471
+
472
+ describe "with several factories files under #{dir}/factories" do
473
+ in_directory_with_files File.join(dir, 'factories', 'post_factory.rb'),
474
+ File.join(dir, 'factories', 'person_factory.rb')
475
+ it_should_behave_like "finds definitions"
476
+ it { should require_definitions_from("#{dir}/factories/post_factory.rb") }
477
+ it { should require_definitions_from("#{dir}/factories/person_factory.rb") }
478
+ end
479
+
480
+ describe "with nested and unnested factories files under #{dir}" do
481
+ in_directory_with_files File.join(dir, 'factories.rb'),
482
+ File.join(dir, 'factories', 'post_factory.rb'),
483
+ File.join(dir, 'factories', 'person_factory.rb')
484
+ it_should_behave_like "finds definitions"
485
+ it { should require_definitions_from("#{dir}/factories.rb") }
486
+ it { should require_definitions_from("#{dir}/factories/post_factory.rb") }
487
+ it { should require_definitions_from("#{dir}/factories/person_factory.rb") }
488
+ end
489
+ end
490
+ end