snowblink-factory_girl 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
- data/Changelog +29 -0
- data/LICENSE +19 -0
- data/README.textile +154 -0
- data/Rakefile +76 -0
- data/lib/factory_girl.rb +33 -0
- data/lib/factory_girl/aliases.rb +39 -0
- data/lib/factory_girl/attribute.rb +24 -0
- data/lib/factory_girl/factory.rb +258 -0
- data/lib/factory_girl/sequence.rb +58 -0
- data/test/aliases_test.rb +29 -0
- data/test/attribute_test.rb +32 -0
- data/test/factory_test.rb +410 -0
- data/test/integration_test.rb +147 -0
- data/test/models.rb +42 -0
- data/test/sequence_test.rb +76 -0
- data/test/test_helper.rb +11 -0
- metadata +79 -0
@@ -0,0 +1,58 @@
|
|
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
|
+
class << self
|
19
|
+
attr_accessor :sequences #:nodoc:
|
20
|
+
end
|
21
|
+
self.sequences = {}
|
22
|
+
|
23
|
+
# Defines a new sequence that can be used to generate unique values in a specific format.
|
24
|
+
#
|
25
|
+
# Arguments:
|
26
|
+
# name: (Symbol)
|
27
|
+
# A unique name for this sequence. This name will be referenced when
|
28
|
+
# calling next to generate new values from this sequence.
|
29
|
+
# block: (Proc)
|
30
|
+
# The code to generate each value in the sequence. This block will be
|
31
|
+
# called with a unique number each time a value in the sequence is to be
|
32
|
+
# generated. The block should return the generated value for the
|
33
|
+
# sequence.
|
34
|
+
#
|
35
|
+
# Example:
|
36
|
+
#
|
37
|
+
# Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
|
38
|
+
def self.sequence (name, &block)
|
39
|
+
self.sequences[name] = Sequence.new(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generates and returns the next value in a sequence.
|
43
|
+
#
|
44
|
+
# Arguments:
|
45
|
+
# name: (Symbol)
|
46
|
+
# The name of the sequence that a value should be generated for.
|
47
|
+
#
|
48
|
+
# Returns:
|
49
|
+
# The next value in the sequence. (Object)
|
50
|
+
def self.next (sequence)
|
51
|
+
unless self.sequences.key?(sequence)
|
52
|
+
raise "No such sequence: #{sequence}"
|
53
|
+
end
|
54
|
+
|
55
|
+
self.sequences[sequence].next
|
56
|
+
end
|
57
|
+
|
58
|
+
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,32 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class AttributeTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "an attribute" do
|
6
|
+
setup do
|
7
|
+
@name = :user
|
8
|
+
@attr = Factory::Attribute.new(@name)
|
9
|
+
end
|
10
|
+
|
11
|
+
should "have a name" do
|
12
|
+
assert_equal @name, @attr.name
|
13
|
+
end
|
14
|
+
|
15
|
+
should "do nothing when being added to a proxy" do
|
16
|
+
@proxy = mock('proxy')
|
17
|
+
@proxy.expects(:set).never
|
18
|
+
@attr.add_to(@proxy)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
should "raise an error when defining an attribute writer" do
|
23
|
+
assert_raise Factory::AttributeDefinitionError do
|
24
|
+
Factory::Attribute.new('test=')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
should "convert names to symbols" do
|
29
|
+
assert_equal :name, Factory::Attribute.new('name').name
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,410 @@
|
|
1
|
+
require(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class FactoryTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "defining a factory" do
|
6
|
+
setup do
|
7
|
+
@name = :user
|
8
|
+
@factory = mock('factory')
|
9
|
+
@factory.stubs(:factory_name).returns(@name)
|
10
|
+
@options = { :class => 'magic' }
|
11
|
+
Factory.stubs(:new).returns(@factory)
|
12
|
+
end
|
13
|
+
|
14
|
+
should "create a new factory using the specified name and options" do
|
15
|
+
Factory.expects(:new).with(@name, @options).returns(@factory)
|
16
|
+
Factory.define(@name, @options) {|f| }
|
17
|
+
end
|
18
|
+
|
19
|
+
should "pass the factory do the block" do
|
20
|
+
yielded = nil
|
21
|
+
Factory.define(@name) do |y|
|
22
|
+
yielded = y
|
23
|
+
end
|
24
|
+
assert_equal @factory, yielded
|
25
|
+
end
|
26
|
+
|
27
|
+
should "add the factory to the list of factories" do
|
28
|
+
Factory.define(@name) {|f| }
|
29
|
+
assert_equal Factory.factories[@name],
|
30
|
+
@factory,
|
31
|
+
"Factories: #{Factory.factories.inspect}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "a factory" do
|
36
|
+
setup do
|
37
|
+
@name = :user
|
38
|
+
@class = User
|
39
|
+
@factory = Factory.new(@name)
|
40
|
+
end
|
41
|
+
|
42
|
+
should "have a factory name" do
|
43
|
+
assert_equal @name, @factory.factory_name
|
44
|
+
end
|
45
|
+
|
46
|
+
should "have a build class" do
|
47
|
+
assert_equal @class, @factory.build_class
|
48
|
+
end
|
49
|
+
|
50
|
+
should "not allow the same attribute to be added twice" do
|
51
|
+
assert_raise(Factory::AttributeDefinitionError) do
|
52
|
+
2.times { @factory.add_attribute :first_name }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
should "add a static attribute when an attribute is defined with a value" do
|
57
|
+
attribute = mock('attribute', :name => :name)
|
58
|
+
Factory::Attribute::Static.
|
59
|
+
expects(:new).
|
60
|
+
with(:name, 'value').
|
61
|
+
returns(attribute)
|
62
|
+
@factory.add_attribute(:name, 'value')
|
63
|
+
end
|
64
|
+
|
65
|
+
should "add a dynamic attribute when an attribute is defined with a block" do
|
66
|
+
attribute = mock('attribute', :name => :name)
|
67
|
+
block = lambda {}
|
68
|
+
Factory::Attribute::Dynamic.
|
69
|
+
expects(:new).
|
70
|
+
with(:name, block).
|
71
|
+
returns(attribute)
|
72
|
+
@factory.add_attribute(:name, &block)
|
73
|
+
end
|
74
|
+
|
75
|
+
should "raise for an attribute with a value and a block" do
|
76
|
+
assert_raise(Factory::AttributeDefinitionError) do
|
77
|
+
@factory.add_attribute(:name, 'value') {}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "after adding an attribute" do
|
82
|
+
setup do
|
83
|
+
@attribute = mock('attribute')
|
84
|
+
@proxy = mock('proxy')
|
85
|
+
|
86
|
+
@attribute. stubs(:name). returns(:name)
|
87
|
+
@attribute. stubs(:add_to)
|
88
|
+
@proxy. stubs(:set)
|
89
|
+
@proxy. stubs(:result).returns('result')
|
90
|
+
Factory::Attribute::Static.stubs(:new). returns(@attribute)
|
91
|
+
Factory::Proxy::Build. stubs(:new). returns(@proxy)
|
92
|
+
|
93
|
+
@factory.add_attribute(:name, 'value')
|
94
|
+
end
|
95
|
+
|
96
|
+
should "create the right proxy using the build class when running" do
|
97
|
+
Factory::Proxy::Build.
|
98
|
+
expects(:new).
|
99
|
+
with(@factory.build_class).
|
100
|
+
returns(@proxy)
|
101
|
+
@factory.run(Factory::Proxy::Build, {})
|
102
|
+
end
|
103
|
+
|
104
|
+
should "add the attribute to the proxy when running" do
|
105
|
+
@attribute.expects(:add_to).with(@proxy)
|
106
|
+
@factory.run(Factory::Proxy::Build, {})
|
107
|
+
end
|
108
|
+
|
109
|
+
should "return the result from the proxy when running" do
|
110
|
+
@proxy.expects(:result).with().returns('result')
|
111
|
+
assert_equal 'result',
|
112
|
+
@factory.run(Factory::Proxy::Build, {})
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
should "add an association without a factory name or overrides" do
|
117
|
+
factory = Factory.new(:post)
|
118
|
+
name = :user
|
119
|
+
attr = 'attribute'
|
120
|
+
Factory::Attribute::Association.
|
121
|
+
expects(:new).
|
122
|
+
with(name, name, {}).
|
123
|
+
returns(attr)
|
124
|
+
factory.association(name)
|
125
|
+
assert factory.attributes.include?(attr)
|
126
|
+
end
|
127
|
+
|
128
|
+
should "add an association with overrides" do
|
129
|
+
factory = Factory.new(:post)
|
130
|
+
name = :user
|
131
|
+
attr = 'attribute'
|
132
|
+
overrides = { :first_name => 'Ben' }
|
133
|
+
Factory::Attribute::Association.
|
134
|
+
expects(:new).
|
135
|
+
with(name, name, overrides).
|
136
|
+
returns(attr)
|
137
|
+
factory.association(name, overrides)
|
138
|
+
assert factory.attributes.include?(attr)
|
139
|
+
end
|
140
|
+
|
141
|
+
should "add an association with a factory name" do
|
142
|
+
factory = Factory.new(:post)
|
143
|
+
attr = 'attribute'
|
144
|
+
Factory::Attribute::Association.
|
145
|
+
expects(:new).
|
146
|
+
with(:author, :user, {}).
|
147
|
+
returns(attr)
|
148
|
+
factory.association(:author, :factory => :user)
|
149
|
+
assert factory.attributes.include?(attr)
|
150
|
+
end
|
151
|
+
|
152
|
+
should "add an association with a factory name and overrides" do
|
153
|
+
factory = Factory.new(:post)
|
154
|
+
attr = 'attribute'
|
155
|
+
Factory::Attribute::Association.
|
156
|
+
expects(:new).
|
157
|
+
with(:author, :user, :first_name => 'Ben').
|
158
|
+
returns(attr)
|
159
|
+
factory.association(:author, :factory => :user, :first_name => 'Ben')
|
160
|
+
assert factory.attributes.include?(attr)
|
161
|
+
end
|
162
|
+
|
163
|
+
should "add an attribute using the method name when passed an undefined method" do
|
164
|
+
attr = mock('attribute', :name => :name)
|
165
|
+
block = lambda {}
|
166
|
+
Factory::Attribute::Static.
|
167
|
+
expects(:new).
|
168
|
+
with(:name, 'value').
|
169
|
+
returns(attr)
|
170
|
+
@factory.send(:name, 'value')
|
171
|
+
assert @factory.attributes.include?(attr)
|
172
|
+
end
|
173
|
+
|
174
|
+
context "when overriding generated attributes with a hash" do
|
175
|
+
setup do
|
176
|
+
@attr = :name
|
177
|
+
@value = 'The price is right!'
|
178
|
+
@hash = { @attr => @value }
|
179
|
+
end
|
180
|
+
|
181
|
+
should "return the overridden value in the generated attributes" do
|
182
|
+
@factory.add_attribute(@attr, 'The price is wrong, Bob!')
|
183
|
+
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
184
|
+
assert_equal @value, result[@attr]
|
185
|
+
end
|
186
|
+
|
187
|
+
should "not call a lazy attribute block for an overridden attribute" do
|
188
|
+
@factory.add_attribute(@attr) { flunk }
|
189
|
+
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
190
|
+
end
|
191
|
+
|
192
|
+
should "override a symbol parameter with a string parameter" do
|
193
|
+
@factory.add_attribute(@attr, 'The price is wrong, Bob!')
|
194
|
+
@hash = { @attr.to_s => @value }
|
195
|
+
result = @factory.run(Factory::Proxy::AttributesFor, @hash)
|
196
|
+
assert_equal @value, result[@attr]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "overriding an attribute with an alias" do
|
201
|
+
setup do
|
202
|
+
@factory.add_attribute(:test, 'original')
|
203
|
+
Factory.alias(/(.*)_alias/, '\1')
|
204
|
+
@result = @factory.run(Factory::Proxy::AttributesFor,
|
205
|
+
:test_alias => 'new')
|
206
|
+
end
|
207
|
+
|
208
|
+
should "use the passed in value for the alias" do
|
209
|
+
assert_equal 'new', @result[:test_alias]
|
210
|
+
end
|
211
|
+
|
212
|
+
should "discard the predefined value for the attribute" do
|
213
|
+
assert_nil @result[:test]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
should "guess the build class from the factory name" do
|
218
|
+
assert_equal User, @factory.build_class
|
219
|
+
end
|
220
|
+
|
221
|
+
context "when defined with a custom class" do
|
222
|
+
setup do
|
223
|
+
@class = User
|
224
|
+
@factory = Factory.new(:author, :class => @class)
|
225
|
+
end
|
226
|
+
|
227
|
+
should "use the specified class as the build class" do
|
228
|
+
assert_equal @class, @factory.build_class
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
context "when defined with a class instead of a name" do
|
233
|
+
setup do
|
234
|
+
@class = ArgumentError
|
235
|
+
@name = :argument_error
|
236
|
+
@factory = Factory.new(@class)
|
237
|
+
end
|
238
|
+
|
239
|
+
should "guess the name from the class" do
|
240
|
+
assert_equal @name, @factory.factory_name
|
241
|
+
end
|
242
|
+
|
243
|
+
should "use the class as the build class" do
|
244
|
+
assert_equal @class, @factory.build_class
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context "when defined with a custom class name" do
|
249
|
+
setup do
|
250
|
+
@class = ArgumentError
|
251
|
+
@factory = Factory.new(:author, :class => :argument_error)
|
252
|
+
end
|
253
|
+
|
254
|
+
should "use the specified class as the build class" do
|
255
|
+
assert_equal @class, @factory.build_class
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
context "a factory with a name ending in s" do
|
261
|
+
setup do
|
262
|
+
@name = :business
|
263
|
+
@class = Business
|
264
|
+
@factory = Factory.new(@name)
|
265
|
+
end
|
266
|
+
|
267
|
+
should "have a factory name" do
|
268
|
+
assert_equal @name, @factory.factory_name
|
269
|
+
end
|
270
|
+
|
271
|
+
should "have a build class" do
|
272
|
+
assert_equal @class, @factory.build_class
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
context "a factory with a string for a name" do
|
277
|
+
setup do
|
278
|
+
@name = :user
|
279
|
+
@factory = Factory.new(@name.to_s) {}
|
280
|
+
end
|
281
|
+
|
282
|
+
should "convert the string to a symbol" do
|
283
|
+
assert_equal @name, @factory.factory_name
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "a factory defined with a string name" do
|
288
|
+
setup do
|
289
|
+
Factory.factories = {}
|
290
|
+
@name = :user
|
291
|
+
@factory = Factory.define(@name.to_s) {}
|
292
|
+
end
|
293
|
+
|
294
|
+
should "store the factory using a symbol" do
|
295
|
+
assert_equal @factory, Factory.factories[@name]
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
context "after defining a factory" do
|
300
|
+
setup do
|
301
|
+
@name = :user
|
302
|
+
@factory = mock('factory')
|
303
|
+
|
304
|
+
Factory.factories[@name] = @factory
|
305
|
+
end
|
306
|
+
|
307
|
+
teardown { Factory.factories.clear }
|
308
|
+
|
309
|
+
should "use Proxy::AttributesFor for Factory.attributes_for" do
|
310
|
+
@factory.
|
311
|
+
expects(:run).
|
312
|
+
with(Factory::Proxy::AttributesFor, :attr => 'value').
|
313
|
+
returns('result')
|
314
|
+
assert_equal 'result', Factory.attributes_for(@name, :attr => 'value')
|
315
|
+
end
|
316
|
+
|
317
|
+
should "use Proxy::Build for Factory.build" do
|
318
|
+
@factory.
|
319
|
+
expects(:run).
|
320
|
+
with(Factory::Proxy::Build, :attr => 'value').
|
321
|
+
returns('result')
|
322
|
+
assert_equal 'result', Factory.build(@name, :attr => 'value')
|
323
|
+
end
|
324
|
+
|
325
|
+
should "use Proxy::Create for Factory.create" do
|
326
|
+
@factory.
|
327
|
+
expects(:run).
|
328
|
+
with(Factory::Proxy::Create, :attr => 'value').
|
329
|
+
returns('result')
|
330
|
+
assert_equal 'result', Factory.create(@name, :attr => 'value')
|
331
|
+
end
|
332
|
+
|
333
|
+
should "use Proxy::Create for the global Factory method" do
|
334
|
+
@factory.
|
335
|
+
expects(:run).
|
336
|
+
with(Factory::Proxy::Create, :attr => 'value').
|
337
|
+
returns('result')
|
338
|
+
assert_equal 'result', Factory(@name, :attr => 'value')
|
339
|
+
end
|
340
|
+
|
341
|
+
[:build, :create, :attributes_for].each do |method|
|
342
|
+
should "raise an ArgumentError on #{method} with a nonexistant factory" do
|
343
|
+
assert_raise(ArgumentError) { Factory.send(method, :bogus) }
|
344
|
+
end
|
345
|
+
|
346
|
+
should "recognize either 'name' or :name for Factory.#{method}" do
|
347
|
+
@factory.stubs(:run)
|
348
|
+
assert_nothing_raised { Factory.send(method, @name.to_s) }
|
349
|
+
assert_nothing_raised { Factory.send(method, @name.to_sym) }
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def self.context_in_directory_with_files(*files)
|
355
|
+
context "in a directory with #{files.to_sentence}" do
|
356
|
+
setup do
|
357
|
+
@pwd = Dir.pwd
|
358
|
+
@tmp_dir = File.join(File.dirname(__FILE__), 'tmp')
|
359
|
+
FileUtils.mkdir_p @tmp_dir
|
360
|
+
Dir.chdir(@tmp_dir)
|
361
|
+
|
362
|
+
files.each do |file|
|
363
|
+
FileUtils.mkdir_p File.dirname(file)
|
364
|
+
FileUtils.touch file
|
365
|
+
Factory.stubs(:require).with(file)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
teardown do
|
370
|
+
Dir.chdir(@pwd)
|
371
|
+
FileUtils.rm_rf(@tmp_dir)
|
372
|
+
end
|
373
|
+
|
374
|
+
yield
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def self.should_require_definitions_from(file)
|
379
|
+
should "load definitions from #{file}" do
|
380
|
+
Factory.expects(:require).with(file)
|
381
|
+
Factory.find_definitions
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
context_in_directory_with_files 'factories.rb' do
|
386
|
+
should_require_definitions_from 'factories.rb'
|
387
|
+
end
|
388
|
+
|
389
|
+
%w(spec test).each do |dir|
|
390
|
+
context_in_directory_with_files File.join(dir, 'factories.rb') do
|
391
|
+
should_require_definitions_from "#{dir}/factories.rb"
|
392
|
+
end
|
393
|
+
|
394
|
+
context_in_directory_with_files File.join(dir, 'factories', 'post_factory.rb') do
|
395
|
+
should_require_definitions_from "#{dir}/factories/post_factory.rb"
|
396
|
+
end
|
397
|
+
|
398
|
+
context_in_directory_with_files File.join(dir, 'factories', 'post_factory.rb'), File.join(dir, 'factories', 'person_factory.rb') do
|
399
|
+
should_require_definitions_from "#{dir}/factories/post_factory.rb"
|
400
|
+
should_require_definitions_from "#{dir}/factories/person_factory.rb"
|
401
|
+
end
|
402
|
+
|
403
|
+
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
|
404
|
+
should_require_definitions_from "#{dir}/factories.rb"
|
405
|
+
should_require_definitions_from "#{dir}/factories/post_factory.rb"
|
406
|
+
should_require_definitions_from "#{dir}/factories/person_factory.rb"
|
407
|
+
end
|
408
|
+
end
|
409
|
+
|
410
|
+
end
|