factory_girl 1.3.3 → 2.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/README.rdoc +68 -60
  2. data/features/support/test.db +0 -0
  3. data/lib/factory_girl.rb +6 -12
  4. data/lib/factory_girl/aliases.rb +2 -31
  5. data/lib/factory_girl/attribute.rb +1 -1
  6. data/lib/factory_girl/attribute/association.rb +1 -1
  7. data/lib/factory_girl/attribute/callback.rb +1 -1
  8. data/lib/factory_girl/attribute/dynamic.rb +3 -3
  9. data/lib/factory_girl/attribute/static.rb +1 -1
  10. data/lib/factory_girl/definition_proxy.rb +180 -0
  11. data/lib/factory_girl/deprecated.rb +18 -0
  12. data/lib/factory_girl/factory.rb +120 -355
  13. data/lib/factory_girl/find_definitions.rb +25 -0
  14. data/lib/factory_girl/proxy.rb +4 -6
  15. data/lib/factory_girl/proxy/attributes_for.rb +1 -1
  16. data/lib/factory_girl/proxy/build.rb +7 -5
  17. data/lib/factory_girl/proxy/create.rb +1 -1
  18. data/lib/factory_girl/proxy/stub.rb +11 -5
  19. data/lib/factory_girl/rails2.rb +1 -1
  20. data/lib/factory_girl/sequence.rb +5 -40
  21. data/lib/factory_girl/step_definitions.rb +7 -7
  22. data/lib/factory_girl/syntax.rb +7 -7
  23. data/lib/factory_girl/syntax/blueprint.rb +5 -4
  24. data/lib/factory_girl/syntax/default.rb +31 -0
  25. data/lib/factory_girl/syntax/generate.rb +13 -8
  26. data/lib/factory_girl/syntax/make.rb +8 -6
  27. data/lib/factory_girl/syntax/sham.rb +11 -8
  28. data/lib/factory_girl/syntax/vintage.rb +196 -0
  29. data/lib/factory_girl/version.rb +4 -0
  30. data/spec/acceptance/acceptance_spec.rb +43 -60
  31. data/spec/acceptance/syntax/blueprint_spec.rb +1 -5
  32. data/spec/acceptance/syntax/generate_spec.rb +1 -4
  33. data/spec/acceptance/syntax/make_spec.rb +1 -4
  34. data/spec/acceptance/syntax/sham_spec.rb +9 -7
  35. data/spec/acceptance/syntax/vintage_spec.rb +184 -0
  36. data/spec/factory_girl/aliases_spec.rb +5 -5
  37. data/spec/factory_girl/attribute/association_spec.rb +3 -3
  38. data/spec/factory_girl/attribute/callback_spec.rb +3 -3
  39. data/spec/factory_girl/attribute/dynamic_spec.rb +20 -9
  40. data/spec/factory_girl/attribute/static_spec.rb +5 -5
  41. data/spec/factory_girl/attribute_spec.rb +5 -5
  42. data/spec/factory_girl/definition_proxy_spec.rb +138 -0
  43. data/spec/factory_girl/deprecated_spec.rb +66 -0
  44. data/spec/factory_girl/factory_spec.rb +283 -566
  45. data/spec/factory_girl/find_definitions_spec.rb +89 -0
  46. data/spec/factory_girl/proxy/attributes_for_spec.rb +2 -2
  47. data/spec/factory_girl/proxy/build_spec.rb +17 -12
  48. data/spec/factory_girl/proxy/create_spec.rb +17 -12
  49. data/spec/factory_girl/proxy/stub_spec.rb +6 -5
  50. data/spec/factory_girl/proxy_spec.rb +2 -2
  51. data/spec/factory_girl/sequence_spec.rb +15 -38
  52. data/spec/spec_helper.rb +4 -0
  53. metadata +28 -11
data/README.rdoc CHANGED
@@ -1,10 +1,15 @@
1
+ = NOTE: this documentation is for the beta version of factory_girl 2
2
+
3
+ Up-to-date documentation for the stable branch can be found here:
4
+
5
+ http://github.com/thoughtbot/factory_girl/tree/1.3.x
6
+
1
7
  = factory_girl
2
8
 
3
9
  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
10
 
5
- If you want to use factory_girl with Rails 3, use the factory_girl_rails gem, not this one.
6
-
7
- If you want to use factory_girl with Rails versions prior to Rails 3, use version 1.2.4.
11
+ If you want to use factory_girl with Rails 3, see
12
+ http://github.com/thoughtbot/factory_girl_rails
8
13
 
9
14
  == Download
10
15
 
@@ -18,24 +23,26 @@ Gem:
18
23
  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:
19
24
 
20
25
  # This will guess the User class
21
- Factory.define :user do |u|
22
- u.first_name 'John'
23
- u.last_name 'Doe'
24
- u.admin false
25
- end
26
-
27
- # This will use the User class (Admin would have been guessed)
28
- Factory.define :admin, :class => User do |u|
29
- u.first_name 'Admin'
30
- u.last_name 'User'
31
- u.admin true
32
- end
33
-
34
- # The same, but using a string instead of class constant
35
- Factory.define :admin, :class => 'user' do |u|
36
- u.first_name 'Admin'
37
- u.last_name 'User'
38
- u.admin true
26
+ FactoryGirl.define :user do
27
+ factory :user do
28
+ first_name 'John'
29
+ last_name 'Doe'
30
+ admin false
31
+ end
32
+
33
+ # This will use the User class (Admin would have been guessed)
34
+ factory :admin, :class => User do
35
+ first_name 'Admin'
36
+ last_name 'User'
37
+ admin true
38
+ end
39
+
40
+ # The same, but using a string instead of class constant
41
+ factory :admin, :class => 'user' do
42
+ first_name 'Admin'
43
+ last_name 'User'
44
+ admin true
45
+ end
39
46
  end
40
47
 
41
48
  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.
@@ -74,7 +81,7 @@ You can use the Factory method as a shortcut for the default build strategy:
74
81
  The default strategy can be overriden:
75
82
 
76
83
  # Now same as Factory.build(:user)
77
- Factory.define :user, :default_strategy => :build do |u|
84
+ factory :user, :default_strategy => :build do
78
85
  ...
79
86
  end
80
87
 
@@ -91,19 +98,19 @@ No matter which strategy is used, it's possible to override the defined attribut
91
98
 
92
99
  Most factory attributes can be added using static values that are evaluated when the factory is defined, but some attributes (such as associations and other attributes that must be dynamically generated) will need values assigned each time an instance is generated. These "lazy" attributes can be added by passing a block instead of a parameter:
93
100
 
94
- Factory.define :user do |u|
101
+ factory :user do
95
102
  # ...
96
- u.activation_code { User.generate_activation_code }
103
+ activation_code { User.generate_activation_code }
97
104
  end
98
105
 
99
106
  == Dependent Attributes
100
107
 
101
108
  Attributes can be based on the values of other attributes using the proxy that is yieled to lazy attribute blocks:
102
109
 
103
- Factory.define :user do |u|
104
- u.first_name 'Joe'
105
- u.last_name 'Blow'
106
- u.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
110
+ factory :user do
111
+ first_name 'Joe'
112
+ last_name 'Blow'
113
+ email { "#{first_name}.#{last_name}@example.com".downcase }
107
114
  end
108
115
 
109
116
  Factory(:user, :last_name => 'Doe').email
@@ -114,9 +121,16 @@ Attributes can be based on the values of other attributes using the proxy that i
114
121
  Associated instances can be generated by using the association method when
115
122
  defining a lazy attribute:
116
123
 
117
- Factory.define :post do |p|
124
+ factory :post do
125
+ # ...
126
+ author
127
+ end
128
+
129
+ You can also specify a different factory or override attributes:
130
+
131
+ factory :post do
118
132
  # ...
119
- p.author {|author| author.association(:user, :last_name => 'Writely') }
133
+ association :author, :factory => :user, :last_name => 'Writely'
120
134
  end
121
135
 
122
136
  The behavior of the association method varies depending on the build strategy used for the parent object.
@@ -131,18 +145,6 @@ The behavior of the association method varies depending on the build strategy us
131
145
  post.new_record? # => true
132
146
  post.author.new_record # => false
133
147
 
134
- Because this pattern is so common, a prettier syntax is available for defining
135
- associations:
136
-
137
- # The following definitions are equivalent:
138
- Factory.define :post do |p|
139
- p.author {|a| a.association(:user) }
140
- end
141
-
142
- Factory.define :post do |p|
143
- p.association :author, :factory => :user
144
- end
145
-
146
148
  If the factory name is the same as the association name, the factory name can
147
149
  be left out.
148
150
 
@@ -150,15 +152,15 @@ be left out.
150
152
 
151
153
  You can easily create multiple factories for the same class without repeating common attributes by using inheritance:
152
154
 
153
- Factory.define :post do |p|
155
+ factory :post do
154
156
  # the 'title' attribute is required for all posts
155
- p.title 'A title'
157
+ title 'A title'
156
158
  end
157
159
 
158
- Factory.define :approved_post, :parent => :post do |p|
159
- p.approved true
160
+ factory :approved_post, :parent => :post do
161
+ approved true
160
162
  # the 'approver' association is required for an approved post
161
- p.association :approver, :factory => :user
163
+ association :approver, :factory => :user
162
164
  end
163
165
 
164
166
  == Sequences
@@ -168,7 +170,7 @@ generated using sequences. Sequences are defined by calling Factory.sequence,
168
170
  and values in a sequence are generated by calling Factory.next:
169
171
 
170
172
  # Defines a new sequence
171
- Factory.sequence :email do |n|
173
+ FactoryGirl.sequence :email do |n|
172
174
  "person#{n}@example.com"
173
175
  end
174
176
 
@@ -178,16 +180,22 @@ and values in a sequence are generated by calling Factory.next:
178
180
  Factory.next :email
179
181
  # => "person2@example.com"
180
182
 
181
- Sequences can be used in lazy attributes:
183
+ Sequences can be used as attributes:
184
+
185
+ factory :user do
186
+ email
187
+ end
188
+
189
+ Or in lazy attributes:
182
190
 
183
- Factory.define :user do |f|
184
- f.email { Factory.next(:email) }
191
+ factory :invite do
192
+ invitee { Factory.next(:email) }
185
193
  end
186
194
 
187
195
  And it's also possible to define an in-line sequence that is only used in
188
196
  a particular factory:
189
197
 
190
- Factory.define :user do |f|
198
+ factory :user do
191
199
  f.sequence(:email) {|n| "person#{n}@example.com" }
192
200
  end
193
201
 
@@ -202,24 +210,24 @@ Factory_girl makes available three callbacks for injecting some code:
202
210
  Examples:
203
211
 
204
212
  # Define a factory that calls the generate_hashed_password method after it is built
205
- Factory.define :user do |u|
206
- u.after_build { |user| do_something_to(user) }
213
+ factory :user do
214
+ after_build { |user| do_something_to(user) }
207
215
  end
208
216
 
209
217
  Note that you'll have an instance of the user in the block. This can be useful.
210
218
 
211
219
  You can also define multiple types of callbacks on the same factory:
212
220
 
213
- Factory.define :user do |u|
214
- u.after_build { |user| do_something_to(user) }
215
- u.after_create { |user| do_something_else_to(user) }
221
+ factory :user do
222
+ after_build { |user| do_something_to(user) }
223
+ after_create { |user| do_something_else_to(user) }
216
224
  end
217
225
 
218
226
  Factories can also define any number of the same kind of callback. These callbacks will be executed in the order they are specified:
219
227
 
220
- Factory.define :user do |u|
221
- u.after_create { this_runs_first }
222
- u.after_create { then_this }
228
+ factory :user do
229
+ after_create { this_runs_first }
230
+ after_create { then_this }
223
231
  end
224
232
 
225
233
  Calling Factory.create will invoke both after_build and after_create callbacks.
Binary file
data/lib/factory_girl.rb CHANGED
@@ -11,18 +11,12 @@ require 'factory_girl/attribute/association'
11
11
  require 'factory_girl/attribute/callback'
12
12
  require 'factory_girl/sequence'
13
13
  require 'factory_girl/aliases'
14
-
15
- # Shortcut for Factory.default_strategy.
16
- #
17
- # Example:
18
- # Factory(:user, :name => 'Joe')
19
- def Factory (name, attrs = {})
20
- Factory.default_strategy(name, attrs)
21
- end
22
-
23
- class Factory
24
- VERSION = "1.3.3"
25
- end
14
+ require 'factory_girl/definition_proxy'
15
+ require 'factory_girl/syntax/default'
16
+ require 'factory_girl/syntax/vintage'
17
+ require 'factory_girl/find_definitions'
18
+ require 'factory_girl/deprecated'
19
+ require 'factory_girl/version'
26
20
 
27
21
  if defined?(Rails) && Rails::VERSION::MAJOR == 2
28
22
  require 'factory_girl/rails2'
@@ -1,4 +1,4 @@
1
- class Factory
1
+ module FactoryGirl
2
2
 
3
3
  class << self
4
4
  attr_accessor :aliases #:nodoc:
@@ -8,35 +8,7 @@ class Factory
8
8
  [/(.*)/, '\1_id']
9
9
  ]
10
10
 
11
- # Defines a new alias for attributes.
12
- #
13
- # Arguments:
14
- # * pattern: +Regexp+
15
- # A pattern that will be matched against attributes when looking for
16
- # aliases. Contents captured in the pattern can be used in the alias.
17
- # * replace: +String+
18
- # The alias that results from the matched pattern. Captured strings can
19
- # be substituted like with +String#sub+.
20
- #
21
- # Example:
22
- #
23
- # Factory.alias /(.*)_confirmation/, '\1'
24
- #
25
- # factory_girl starts with aliases for foreign keys, so that a :user
26
- # association can be overridden by a :user_id parameter:
27
- #
28
- # Factory.define :post do |p|
29
- # p.association :user
30
- # end
31
- #
32
- # # The user association will not be built in this example. The user_id
33
- # # will be used instead.
34
- # Factory(:post, :user_id => 1)
35
- def self.alias (pattern, replace)
36
- self.aliases << [pattern, replace]
37
- end
38
-
39
- def self.aliases_for (attribute) #:nodoc:
11
+ def self.aliases_for(attribute) #:nodoc:
40
12
  aliases.collect do |params|
41
13
  pattern, replace = *params
42
14
  if pattern.match(attribute.to_s)
@@ -46,5 +18,4 @@ class Factory
46
18
  end
47
19
  end.compact << attribute
48
20
  end
49
-
50
21
  end
@@ -1,4 +1,4 @@
1
- class Factory
1
+ module FactoryGirl
2
2
 
3
3
  # Raised when defining an invalid attribute:
4
4
  # * Defining an attribute which has a name ending in "="
@@ -1,4 +1,4 @@
1
- class Factory
1
+ module FactoryGirl
2
2
  class Attribute #:nodoc:
3
3
 
4
4
  class Association < Attribute #:nodoc:
@@ -1,4 +1,4 @@
1
- class Factory
1
+ module FactoryGirl
2
2
  class Attribute #:nodoc:
3
3
 
4
4
  class Callback < Attribute #:nodoc:
@@ -1,4 +1,4 @@
1
- class Factory
1
+ module FactoryGirl
2
2
  class Attribute #:nodoc:
3
3
 
4
4
  class Dynamic < Attribute #:nodoc:
@@ -8,8 +8,8 @@ class Factory
8
8
  end
9
9
 
10
10
  def add_to(proxy)
11
- value = @block.arity.zero? ? @block.call : @block.call(proxy)
12
- if Factory::Sequence === value
11
+ value = @block.arity == 1 ? @block.call(proxy) : proxy.instance_eval(&@block)
12
+ if FactoryGirl::Sequence === value
13
13
  raise SequenceAbuseError
14
14
  end
15
15
  proxy.set(name, value)
@@ -1,4 +1,4 @@
1
- class Factory
1
+ module FactoryGirl
2
2
  class Attribute #:nodoc:
3
3
 
4
4
  class Static < Attribute #:nodoc:
@@ -0,0 +1,180 @@
1
+ module FactoryGirl
2
+ class DefinitionProxy
3
+ instance_methods.each do |method|
4
+ undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$|^instance_eval$)/
5
+ end
6
+
7
+ def initialize(factory)
8
+ @factory = factory
9
+ end
10
+
11
+ # Adds an attribute that should be assigned on generated instances for this
12
+ # factory.
13
+ #
14
+ # This method should be called with either a value or block, but not both. If
15
+ # called with a block, the attribute will be generated "lazily," whenever an
16
+ # instance is generated. Lazy attribute blocks will not be called if that
17
+ # attribute is overridden for a specific instance.
18
+ #
19
+ # When defining lazy attributes, an instance of FactoryGirl::Proxy will
20
+ # be yielded, allowing associations to be built using the correct build
21
+ # strategy.
22
+ #
23
+ # Arguments:
24
+ # * name: +Symbol+ or +String+
25
+ # The name of this attribute. This will be assigned using :"#{name}=" for
26
+ # generated instances.
27
+ # * value: +Object+
28
+ # If no block is given, this value will be used for this attribute.
29
+ def add_attribute(name, value = nil, &block)
30
+ if block_given?
31
+ if value
32
+ raise AttributeDefinitionError, "Both value and block given"
33
+ else
34
+ attribute = Attribute::Dynamic.new(name, block)
35
+ end
36
+ else
37
+ attribute = Attribute::Static.new(name, value)
38
+ end
39
+
40
+ @factory.define_attribute(attribute)
41
+ end
42
+
43
+ # Calls add_attribute using the missing method name as the name of the
44
+ # attribute, so that:
45
+ #
46
+ # factory :user do
47
+ # name 'Billy Idol'
48
+ # end
49
+ #
50
+ # and:
51
+ #
52
+ # factory :user do
53
+ # add_attribute :name, 'Billy Idol'
54
+ # end
55
+ #
56
+ # are equivilent.
57
+ #
58
+ # If no argument or block is given, factory_girl will look for a sequence
59
+ # or association with the same name. This means that:
60
+ #
61
+ # factory :user do
62
+ # email { Factory.next(:email) }
63
+ # association :account
64
+ # end
65
+ #
66
+ # and:
67
+ #
68
+ # factory :user do
69
+ # email
70
+ # account
71
+ # end
72
+ #
73
+ # are equivilent.
74
+ def method_missing(name, *args, &block)
75
+ if args.empty? && block.nil?
76
+ if sequence = FactoryGirl.sequences[name]
77
+ add_attribute(name) { sequence.next }
78
+ else
79
+ association(name)
80
+ end
81
+ else
82
+ add_attribute(name, *args, &block)
83
+ end
84
+ end
85
+
86
+ # Adds an attribute that will have unique values generated by a sequence with
87
+ # a specified format.
88
+ #
89
+ # The result of:
90
+ # factory :user do
91
+ # sequence(:email) { |n| "person#{n}@example.com" }
92
+ # end
93
+ #
94
+ # Is equal to:
95
+ # sequence(:email) { |n| "person#{n}@example.com" }
96
+ #
97
+ # factory :user do
98
+ # email { Factory.next(:email) }
99
+ # end
100
+ #
101
+ # Except that no globally available sequence will be defined.
102
+ def sequence(name, start_value = 1, &block)
103
+ sequence = Sequence.new(start_value, &block)
104
+ add_attribute(name) { sequence.next }
105
+ end
106
+
107
+ # Adds an attribute that builds an association. The associated instance will
108
+ # be built using the same build strategy as the parent instance.
109
+ #
110
+ # Example:
111
+ # factory :user do
112
+ # name 'Joey'
113
+ # end
114
+ #
115
+ # factory :post do
116
+ # association :author, :factory => :user
117
+ # end
118
+ #
119
+ # Arguments:
120
+ # * name: +Symbol+
121
+ # The name of this attribute.
122
+ # * options: +Hash+
123
+ #
124
+ # Options:
125
+ # * factory: +Symbol+ or +String+
126
+ # The name of the factory to use when building the associated instance.
127
+ # If no name is given, the name of the attribute is assumed to be the
128
+ # name of the factory. For example, a "user" association will by
129
+ # default use the "user" factory.
130
+ def association(name, options = {})
131
+ factory_name = options.delete(:factory) || name
132
+ @factory.define_attribute(Attribute::Association.new(name, factory_name, options))
133
+ end
134
+
135
+ # Registers an alias for this factory using the given name.
136
+ #
137
+ # Arguments:
138
+ # * name: +Symbol+
139
+ # The name of the alias.
140
+ #
141
+ # Example:
142
+ #
143
+ # factory :user do
144
+ # aliased_as :author
145
+ # end
146
+ #
147
+ # Factory(:author).class
148
+ # # => User
149
+ #
150
+ # Because an attribute defined without a value or block will build an
151
+ # association with the same name, this allows associations to be defined
152
+ # without factories, such as:
153
+ #
154
+ # factory :user do
155
+ # aliased_as :author
156
+ # end
157
+ #
158
+ # factory :post do
159
+ # author
160
+ # end
161
+ #
162
+ # Factory(:post).author.class
163
+ # # => User
164
+ def aliased_as(name)
165
+ FactoryGirl.register_factory(@factory, :as => name)
166
+ end
167
+
168
+ def after_build(&block)
169
+ @factory.add_callback(:after_build, &block)
170
+ end
171
+
172
+ def after_create(&block)
173
+ @factory.add_callback(:after_create, &block)
174
+ end
175
+
176
+ def after_stub(&block)
177
+ @factory.add_callback(:after_stub, &block)
178
+ end
179
+ end
180
+ end