jrun-factory_girl 1.1.3.9999

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,56 @@
1
+ class Factory
2
+
3
+ class Sequence
4
+
5
+ def initialize (&proc) #:nodoc:
6
+ @proc = proc
7
+ @value = 0
8
+ end
9
+
10
+ # Returns the next value for this sequence
11
+ def next
12
+ @value += 1
13
+ @proc.call(@value)
14
+ end
15
+
16
+ end
17
+
18
+ cattr_accessor :sequences #:nodoc:
19
+ self.sequences = {}
20
+
21
+ # Defines a new sequence that can be used to generate unique values in a specific format.
22
+ #
23
+ # Arguments:
24
+ # name: (Symbol)
25
+ # A unique name for this sequence. This name will be referenced when
26
+ # calling next to generate new values from this sequence.
27
+ # block: (Proc)
28
+ # The code to generate each value in the sequence. This block will be
29
+ # called with a unique number each time a value in the sequence is to be
30
+ # generated. The block should return the generated value for the
31
+ # sequence.
32
+ #
33
+ # Example:
34
+ #
35
+ # Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
36
+ def self.sequence (name, &block)
37
+ self.sequences[name] = Sequence.new(&block)
38
+ end
39
+
40
+ # Generates and returns the next value in a sequence.
41
+ #
42
+ # Arguments:
43
+ # name: (Symbol)
44
+ # The name of the sequence that a value should be generated for.
45
+ #
46
+ # Returns:
47
+ # The next value in the sequence. (Object)
48
+ def self.next (sequence)
49
+ unless self.sequences.key?(sequence)
50
+ raise "No such sequence: #{sequence}"
51
+ end
52
+
53
+ self.sequences[sequence].next
54
+ end
55
+
56
+ end
@@ -0,0 +1,29 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AliasesTest < Test::Unit::TestCase
4
+
5
+ should "include an attribute as an alias for itself by default" do
6
+ assert Factory.aliases_for(:test).include?(:test)
7
+ end
8
+
9
+ should "include the root of a foreign key as an alias by default" do
10
+ assert Factory.aliases_for(:test_id).include?(:test)
11
+ end
12
+
13
+ should "include an attribute's foreign key as an alias by default" do
14
+ assert Factory.aliases_for(:test).include?(:test_id)
15
+ end
16
+
17
+ context "after adding an alias" do
18
+
19
+ setup do
20
+ Factory.alias(/(.*)_suffix/, '\1')
21
+ end
22
+
23
+ should "return the alias in the aliases list" do
24
+ assert Factory.aliases_for(:test_suffix).include?(:test)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,101 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AttributeProxyTest < Test::Unit::TestCase
4
+
5
+ context "an association proxy" do
6
+
7
+ setup do
8
+ @factory = mock('factory')
9
+ @attr = :user
10
+ @attrs = { :first_name => 'John' }
11
+ @strategy = :create
12
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
13
+ end
14
+
15
+ should "have a factory" do
16
+ assert_equal @factory, @proxy.factory
17
+ end
18
+
19
+ should "have an attribute name" do
20
+ assert_equal @attr, @proxy.attribute_name
21
+ end
22
+
23
+ should "have a build strategy" do
24
+ assert_equal @strategy, @proxy.strategy
25
+ end
26
+
27
+ should "have attributes" do
28
+ assert_equal @attrs, @proxy.current_values
29
+ end
30
+
31
+ context "building an association" do
32
+
33
+ setup do
34
+ @association = mock('built-user')
35
+ @name = :user
36
+ @attribs = { :first_name => 'Billy' }
37
+
38
+ Factory.stubs(@strategy).returns(@association)
39
+ end
40
+
41
+ should "delegate to the appropriate method on Factory" do
42
+ Factory.expects(@strategy).with(@name, @attribs).returns(@association)
43
+ @proxy.association(@name, @attribs)
44
+ end
45
+
46
+ should "return the built association" do
47
+ assert_equal @association, @proxy.association(@name)
48
+ end
49
+
50
+ end
51
+
52
+ context "building an association using the attributes_for strategy" do
53
+
54
+ setup do
55
+ @strategy = :attributes_for
56
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
57
+ end
58
+
59
+ should "not build the association" do
60
+ Factory.expects(@strategy).never
61
+ @proxy.association(:user)
62
+ end
63
+
64
+ should "return nil for the association" do
65
+ Factory.stubs(@strategy).returns(:user)
66
+ assert_nil @proxy.association(:user)
67
+ end
68
+
69
+ end
70
+
71
+ context "fetching the value of an attribute" do
72
+
73
+ setup do
74
+ @attr = :first_name
75
+ end
76
+
77
+ should "return the correct value" do
78
+ assert_equal @attrs[@attr], @proxy.value_for(@attr)
79
+ end
80
+
81
+ should "call value_for for undefined methods" do
82
+ assert_equal @attrs[@attr], @proxy.send(@attr)
83
+ end
84
+
85
+ end
86
+
87
+ context "fetching the value of an undefined attribute" do
88
+
89
+ setup do
90
+ @attr = :beachball
91
+ end
92
+
93
+ should "raise an ArgumentError" do
94
+ assert_raise(ArgumentError) { @proxy.value_for(@attr) }
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
@@ -0,0 +1,70 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AttributeTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @proxy = mock('attribute-proxy')
7
+ end
8
+
9
+ context "an attribute" do
10
+
11
+ setup do
12
+ @name = :user
13
+ @attr = Factory::Attribute.new(@name, 'test', nil)
14
+ end
15
+
16
+ should "have a name" do
17
+ assert_equal @name, @attr.name
18
+ end
19
+
20
+ end
21
+
22
+ context "an attribute with a static value" do
23
+
24
+ setup do
25
+ @value = 'test'
26
+ @attr = Factory::Attribute.new(:user, @value, nil)
27
+ end
28
+
29
+ should "return the value" do
30
+ assert_equal @value, @attr.value(@proxy)
31
+ end
32
+
33
+ end
34
+
35
+ context "an attribute with a lazy value" do
36
+
37
+ setup do
38
+ @block = lambda { 'value' }
39
+ @attr = Factory::Attribute.new(:user, nil, @block)
40
+ end
41
+
42
+ should "call the block to return a value" do
43
+ assert_equal 'value', @attr.value(@proxy)
44
+ end
45
+
46
+ should "yield the attribute proxy to the block" do
47
+ @block = lambda {|a| a }
48
+ @attr = Factory::Attribute.new(:user, nil, @block)
49
+ assert_equal @proxy, @attr.value(@proxy)
50
+ end
51
+
52
+ end
53
+
54
+ should "raise an error when defining an attribute writer" do
55
+ assert_raise Factory::AttributeDefinitionError do
56
+ Factory::Attribute.new('test=', nil, nil)
57
+ end
58
+ end
59
+
60
+ should "not allow attributes to be added with both a value parameter and a block" do
61
+ assert_raise(Factory::AttributeDefinitionError) do
62
+ Factory::Attribute.new(:name, 'value', lambda {})
63
+ end
64
+ end
65
+
66
+ should "convert names to symbols" do
67
+ assert_equal :name, Factory::Attribute.new('name', nil, nil).name
68
+ end
69
+
70
+ end
@@ -0,0 +1,434 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class FactoryTest < Test::Unit::TestCase
4
+
5
+ def self.should_instantiate_class
6
+
7
+ should "instantiate the build class" do
8
+ assert_kind_of @class, @instance
9
+ end
10
+
11
+ should "assign attributes on the instance" do
12
+ assert_equal @first_name, @instance.first_name
13
+ assert_equal @last_name, @instance.last_name
14
+ end
15
+
16
+ should "override attributes using the passed hash" do
17
+ @value = 'Davis'
18
+ @instance = @factory.build(:first_name => @value)
19
+ assert_equal @value, @instance.first_name
20
+ end
21
+
22
+ end
23
+
24
+ context "defining a factory" do
25
+
26
+ setup do
27
+ @name = :user
28
+ @factory = mock('factory')
29
+ @factory.stubs(:factory_name).returns(@name)
30
+ @options = { :class => 'magic' }
31
+ Factory.stubs(:new).returns(@factory)
32
+ end
33
+
34
+ should "create a new factory using the specified name and options" do
35
+ Factory.expects(:new).with(@name, @options).returns(@factory)
36
+ Factory.define(@name, @options) {|f| }
37
+ end
38
+
39
+ should "pass the factory do the block" do
40
+ yielded = nil
41
+ Factory.define(@name) do |y|
42
+ yielded = y
43
+ end
44
+ assert_equal @factory, yielded
45
+ end
46
+
47
+ should "add the factory to the list of factories" do
48
+ Factory.define(@name) {|f| }
49
+ assert_equal Factory.factories[@name],
50
+ @factory,
51
+ "Factories: #{Factory.factories.inspect}"
52
+ end
53
+
54
+ end
55
+
56
+ context "a factory" do
57
+
58
+ setup do
59
+ @name = :user
60
+ @class = User
61
+ @factory = Factory.new(@name)
62
+ end
63
+
64
+ should "have a factory name" do
65
+ assert_equal @name, @factory.factory_name
66
+ end
67
+
68
+ should "have a build class" do
69
+ assert_equal @class, @factory.build_class
70
+ end
71
+
72
+ should "not allow the same attribute to be added twice" do
73
+ assert_raise(Factory::AttributeDefinitionError) do
74
+ 2.times { @factory.add_attribute @name }
75
+ end
76
+ end
77
+
78
+ context "when adding an attribute with a value parameter" do
79
+
80
+ setup do
81
+ @attr = :name
82
+ @value = 'Elvis lives!'
83
+ @factory.add_attribute(@attr, @value)
84
+ end
85
+
86
+ should "include that value in the generated attributes hash" do
87
+ assert_equal @value, @factory.attributes_for[@attr]
88
+ end
89
+
90
+ end
91
+
92
+ context "when adding an attribute with a block" do
93
+
94
+ setup do
95
+ @attr = :name
96
+ @attrs = {}
97
+ @proxy = mock('attr-proxy')
98
+ Factory::AttributeProxy.stubs(:new).returns(@proxy)
99
+ end
100
+
101
+ should "not evaluate the block when the attribute is added" do
102
+ @factory.add_attribute(@attr) { flunk }
103
+ end
104
+
105
+ should "evaluate the block when attributes are generated" do
106
+ called = false
107
+ @factory.add_attribute(@attr) do
108
+ called = true
109
+ end
110
+ @factory.attributes_for
111
+ assert called
112
+ end
113
+
114
+ should "use the result of the block as the value of the attribute" do
115
+ value = "Watch out for snakes!"
116
+ @factory.add_attribute(@attr) { value }
117
+ assert_equal value, @factory.attributes_for[@attr]
118
+ end
119
+
120
+ should "build an attribute proxy" do
121
+ Factory::AttributeProxy.expects(:new).with(@factory, @attr, :attributes_for, @attrs)
122
+ @factory.add_attribute(@attr) {}
123
+ @factory.attributes_for
124
+ end
125
+
126
+ should "yield an attribute proxy to the block" do
127
+ yielded = nil
128
+ @factory.add_attribute(@attr) {|y| yielded = y }
129
+ @factory.attributes_for
130
+ assert_equal @proxy, yielded
131
+ end
132
+
133
+ context "when other attributes have previously been defined" do
134
+
135
+ setup do
136
+ @attr = :unimportant
137
+ @attrs = {
138
+ :one => 'whatever',
139
+ :another => 'soup'
140
+ }
141
+ @factory.add_attribute(:one, 'whatever')
142
+ @factory.add_attribute(:another) { 'soup' }
143
+ @factory.add_attribute(@attr) {}
144
+ end
145
+
146
+ should "provide previously set attributes" do
147
+ Factory::AttributeProxy.expects(:new).with(@factory, @attr, :attributes_for, @attrs)
148
+ @factory.attributes_for
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+
155
+ context "when adding an association without a factory name" do
156
+
157
+ setup do
158
+ @factory = Factory.new(:post)
159
+ @name = :user
160
+ @factory.association(@name)
161
+ Post.any_instance.stubs(:user=)
162
+ end
163
+
164
+ should "add an attribute with the name of the association" do
165
+ assert @factory.attributes_for.key?(@name)
166
+ end
167
+
168
+ should "create a block that builds the association" do
169
+ Factory.expects(:build).with(@name, {})
170
+ @factory.build
171
+ end
172
+
173
+ end
174
+
175
+ context "when adding an association with a factory name" do
176
+
177
+ setup do
178
+ @factory = Factory.new(:post)
179
+ @name = :author
180
+ @factory_name = :user
181
+ @factory.association(@name, :factory => @factory_name)
182
+ end
183
+
184
+ should "add an attribute with the name of the association" do
185
+ assert @factory.attributes_for.key?(@name)
186
+ end
187
+
188
+ should "create a block that builds the association" do
189
+ Factory.expects(:build).with(@factory_name, {})
190
+ @factory.build
191
+ end
192
+
193
+ end
194
+
195
+ should "add an attribute using the method name when passed an undefined method" do
196
+ @attr = :first_name
197
+ @value = 'Sugar'
198
+ @factory.send(@attr, @value)
199
+ assert_equal @value, @factory.attributes_for[@attr]
200
+ end
201
+
202
+ should "allow attributes to be added with strings as names" do
203
+ @factory.add_attribute('name', 'value')
204
+ assert_equal 'value', @factory.attributes_for[:name]
205
+ end
206
+
207
+ context "when overriding generated attributes with a hash" do
208
+
209
+ setup do
210
+ @attr = :name
211
+ @value = 'The price is right!'
212
+ @hash = { @attr => @value }
213
+ end
214
+
215
+ should "return the overridden value in the generated attributes" do
216
+ @factory.add_attribute(@attr, 'The price is wrong, Bob!')
217
+ assert_equal @value, @factory.attributes_for(@hash)[@attr]
218
+ end
219
+
220
+ should "not call a lazy attribute block for an overridden attribute" do
221
+ @factory.add_attribute(@attr) { flunk }
222
+ @factory.attributes_for(@hash)
223
+ end
224
+
225
+ should "override a symbol parameter with a string parameter" do
226
+ @factory.add_attribute(@attr, 'The price is wrong, Bob!')
227
+ @hash = { @attr.to_s => @value }
228
+ assert_equal @value, @factory.attributes_for(@hash)[@attr]
229
+ end
230
+
231
+ end
232
+
233
+ context "overriding an attribute with an alias" do
234
+
235
+ setup do
236
+ @factory.add_attribute(:test, 'original')
237
+ Factory.alias(/(.*)_alias/, '\1')
238
+ @result = @factory.attributes_for(:test_alias => 'new')
239
+ end
240
+
241
+ should "use the passed in value for the alias" do
242
+ assert_equal 'new', @result[:test_alias]
243
+ end
244
+
245
+ should "discard the predefined value for the attribute" do
246
+ assert_nil @result[:test]
247
+ end
248
+
249
+ end
250
+
251
+ should "guess the build class from the factory name" do
252
+ assert_equal User, @factory.build_class
253
+ end
254
+
255
+ context "when defined with a custom class" do
256
+
257
+ setup do
258
+ @class = User
259
+ @factory = Factory.new(:author, :class => @class)
260
+ end
261
+
262
+ should "use the specified class as the build class" do
263
+ assert_equal @class, @factory.build_class
264
+ end
265
+
266
+ end
267
+
268
+ context "when defined with a class instead of a name" do
269
+
270
+ setup do
271
+ @class = ArgumentError
272
+ @name = :argument_error
273
+ @factory = Factory.new(@class)
274
+ end
275
+
276
+ should "guess the name from the class" do
277
+ assert_equal @name, @factory.factory_name
278
+ end
279
+
280
+ should "use the class as the build class" do
281
+ assert_equal @class, @factory.build_class
282
+ end
283
+
284
+ end
285
+ context "when defined with a custom class name" do
286
+
287
+ setup do
288
+ @class = ArgumentError
289
+ @factory = Factory.new(:author, :class => :argument_error)
290
+ end
291
+
292
+ should "use the specified class as the build class" do
293
+ assert_equal @class, @factory.build_class
294
+ end
295
+
296
+ end
297
+
298
+ context "with some attributes added" do
299
+
300
+ setup do
301
+ @first_name = 'Billy'
302
+ @last_name = 'Idol'
303
+ @email = 'test@something.com'
304
+
305
+ @factory.add_attribute(:first_name, @first_name)
306
+ @factory.add_attribute(:last_name, @last_name)
307
+ @factory.add_attribute(:email, @email)
308
+ end
309
+
310
+ context "when building an instance" do
311
+
312
+ setup do
313
+ @instance = @factory.build
314
+ end
315
+
316
+ should_instantiate_class
317
+
318
+ should "not save the instance" do
319
+ assert @instance.new_record?
320
+ end
321
+
322
+ end
323
+
324
+ context "when creating an instance" do
325
+
326
+ setup do
327
+ @instance = @factory.create
328
+ end
329
+
330
+ should_instantiate_class
331
+
332
+ should "save the instance" do
333
+ assert !@instance.new_record?
334
+ end
335
+
336
+ end
337
+
338
+ should "raise an ActiveRecord::RecordInvalid error for invalid instances" do
339
+ assert_raise(ActiveRecord::RecordInvalid) do
340
+ @factory.create(:first_name => nil)
341
+ end
342
+ end
343
+
344
+ end
345
+
346
+ end
347
+
348
+ context "a factory with a string for a name" do
349
+
350
+ setup do
351
+ @name = :user
352
+ @factory = Factory.new(@name.to_s) {}
353
+ end
354
+
355
+ should "convert the string to a symbol" do
356
+ assert_equal @name, @factory.factory_name
357
+ end
358
+
359
+ end
360
+
361
+ context "a factory defined with a string name" do
362
+
363
+ setup do
364
+ Factory.factories = {}
365
+ @name = :user
366
+ @factory = Factory.define(@name.to_s) {}
367
+ end
368
+
369
+ should "store the factory using a symbol" do
370
+ assert_equal @factory, Factory.factories[@name]
371
+ end
372
+
373
+ end
374
+
375
+ context "Factory class" do
376
+
377
+ setup do
378
+ @name = :user
379
+ @attrs = { :last_name => 'Override' }
380
+ @first_name = 'Johnny'
381
+ @last_name = 'Winter'
382
+ @class = User
383
+
384
+ Factory.define(@name) do |u|
385
+ u.first_name @first_name
386
+ u.last_name { @last_name }
387
+ u.email 'jwinter@guitar.org'
388
+ end
389
+
390
+ @factory = Factory.factories[@name]
391
+ end
392
+
393
+ [:build, :create, :attributes_for].each do |method|
394
+
395
+ should "delegate the #{method} method to the factory instance" do
396
+ @factory.expects(method).with(@attrs)
397
+ Factory.send(method, @name, @attrs)
398
+ end
399
+
400
+ should "raise an ArgumentError when #{method} is called with a nonexistant factory" do
401
+ assert_raise(ArgumentError) { Factory.send(method, :bogus) }
402
+ end
403
+
404
+ should "recognize either 'name' or :name for Factory.#{method}" do
405
+ assert_nothing_raised { Factory.send(method, @name.to_s) }
406
+ assert_nothing_raised { Factory.send(method, @name.to_sym) }
407
+ end
408
+
409
+ end
410
+
411
+ should "call the create method from the top-level Factory() method" do
412
+ @factory.expects(:create).with(@attrs)
413
+ Factory(@name, @attrs)
414
+ end
415
+
416
+ end
417
+
418
+ Factory.definition_file_paths.each do |file|
419
+ should "automatically load definitions from #{file}.rb" do
420
+ Factory.stubs(:require).raises(LoadError)
421
+ Factory.expects(:require).with(file)
422
+ Factory.find_definitions
423
+ end
424
+ end
425
+
426
+ should "only load the first set of factories detected" do
427
+ first, second, third = Factory.definition_file_paths
428
+ Factory.expects(:require).with(first).raises(LoadError)
429
+ Factory.expects(:require).with(second)
430
+ Factory.expects(:require).with(third).never
431
+ Factory.find_definitions
432
+ end
433
+
434
+ end