factory_bot 4.8.2 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.yardopts +1 -0
- data/GETTING_STARTED.md +226 -129
- data/LICENSE +1 -1
- data/NAME.md +12 -5
- data/NEWS.md +351 -0
- data/README.md +35 -27
- data/lib/factory_bot/aliases.rb +1 -1
- data/lib/factory_bot/attribute/dynamic.rb +1 -0
- data/lib/factory_bot/attribute.rb +4 -39
- data/lib/factory_bot/attribute_assigner.rb +21 -6
- data/lib/factory_bot/attribute_list.rb +2 -1
- data/lib/factory_bot/callback.rb +3 -2
- data/lib/factory_bot/configuration.rb +16 -20
- data/lib/factory_bot/declaration/association.rb +14 -1
- data/lib/factory_bot/declaration/dynamic.rb +3 -1
- data/lib/factory_bot/declaration/implicit.rb +7 -2
- data/lib/factory_bot/declaration.rb +4 -4
- data/lib/factory_bot/declaration_list.rb +1 -1
- data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +1 -1
- data/lib/factory_bot/decorator.rb +5 -1
- data/lib/factory_bot/definition.rb +10 -7
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +65 -41
- data/lib/factory_bot/errors.rb +7 -4
- data/lib/factory_bot/evaluation.rb +1 -1
- data/lib/factory_bot/evaluator.rb +5 -5
- data/lib/factory_bot/factory.rb +8 -8
- data/lib/factory_bot/factory_runner.rb +3 -3
- data/lib/factory_bot/find_definitions.rb +1 -1
- data/lib/factory_bot/internal.rb +104 -0
- data/lib/factory_bot/linter.rb +36 -19
- data/lib/factory_bot/null_factory.rb +4 -1
- data/lib/factory_bot/null_object.rb +2 -2
- data/lib/factory_bot/registry.rb +15 -6
- data/lib/factory_bot/reload.rb +3 -3
- data/lib/factory_bot/sequence.rb +9 -1
- data/lib/factory_bot/strategy/null.rb +2 -4
- data/lib/factory_bot/strategy/stub.rb +32 -36
- data/lib/factory_bot/strategy_calculator.rb +1 -1
- data/lib/factory_bot/strategy_syntax_method_registrar.rb +13 -2
- data/lib/factory_bot/syntax/default.rb +12 -24
- data/lib/factory_bot/syntax/methods.rb +32 -9
- data/lib/factory_bot/syntax.rb +2 -2
- data/lib/factory_bot/trait.rb +6 -3
- data/lib/factory_bot/version.rb +1 -1
- data/lib/factory_bot.rb +103 -134
- metadata +79 -64
- data/.autotest +0 -9
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.simplecov +0 -4
- data/.travis.yml +0 -38
- data/Appraisals +0 -19
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -103
- data/NEWS +0 -293
- data/Rakefile +0 -36
- data/cucumber.yml +0 -1
- data/factory_bot.gemspec +0 -36
- data/factory_girl.gemspec +0 -40
- data/gemfiles/3.2.gemfile +0 -10
- data/gemfiles/3.2.gemfile.lock +0 -105
- data/gemfiles/4.0.gemfile +0 -10
- data/gemfiles/4.0.gemfile.lock +0 -105
- data/gemfiles/4.1.gemfile +0 -10
- data/gemfiles/4.1.gemfile.lock +0 -104
- data/gemfiles/4.2.gemfile +0 -10
- data/gemfiles/4.2.gemfile.lock +0 -104
- data/gemfiles/5.0.gemfile +0 -10
- data/gemfiles/5.0.gemfile.lock +0 -103
- data/lib/factory_bot/attribute/static.rb +0 -16
- data/lib/factory_bot/declaration/static.rb +0 -26
- data/lib/factory_bot/decorator/class_key_hash.rb +0 -28
- data/lib/factory_girl.rb +0 -5
data/GETTING_STARTED.md
CHANGED
@@ -4,16 +4,16 @@ Getting Started
|
|
4
4
|
Update Your Gemfile
|
5
5
|
-------------------
|
6
6
|
|
7
|
-
If you're using Rails
|
7
|
+
If you're using Rails:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem "factory_bot_rails"
|
10
|
+
gem "factory_bot_rails"
|
11
11
|
```
|
12
12
|
|
13
|
-
If you're *not* using Rails
|
13
|
+
If you're *not* using Rails:
|
14
14
|
|
15
15
|
```ruby
|
16
|
-
gem "factory_bot"
|
16
|
+
gem "factory_bot"
|
17
17
|
```
|
18
18
|
|
19
19
|
JRuby users: factory_bot works with JRuby starting with 1.6.7.2 (latest stable, as per July 2012).
|
@@ -28,15 +28,19 @@ Once your Gemfile is updated, you'll want to update your bundle.
|
|
28
28
|
Configure your test suite
|
29
29
|
-------------------------
|
30
30
|
|
31
|
-
|
31
|
+
### RSpec
|
32
|
+
|
33
|
+
If you're using Rails, add the following configuration to `spec/support/factory_bot.rb` and be sure to require that file in `rails_helper.rb`:
|
32
34
|
|
33
35
|
```ruby
|
34
|
-
# spec/support/factory_bot.rb
|
35
36
|
RSpec.configure do |config|
|
36
37
|
config.include FactoryBot::Syntax::Methods
|
37
38
|
end
|
39
|
+
```
|
38
40
|
|
39
|
-
|
41
|
+
If you're *not* using Rails:
|
42
|
+
|
43
|
+
```ruby
|
40
44
|
RSpec.configure do |config|
|
41
45
|
config.include FactoryBot::Syntax::Methods
|
42
46
|
|
@@ -46,13 +50,7 @@ RSpec.configure do |config|
|
|
46
50
|
end
|
47
51
|
```
|
48
52
|
|
49
|
-
|
50
|
-
|
51
|
-
```ruby
|
52
|
-
require 'support/factory_bot'
|
53
|
-
```
|
54
|
-
|
55
|
-
# Test::Unit
|
53
|
+
### Test::Unit
|
56
54
|
|
57
55
|
```ruby
|
58
56
|
class Test::Unit::TestCase
|
@@ -60,14 +58,14 @@ class Test::Unit::TestCase
|
|
60
58
|
end
|
61
59
|
```
|
62
60
|
|
63
|
-
|
61
|
+
### Cucumber
|
64
62
|
|
65
63
|
```ruby
|
66
64
|
# env.rb (Rails example location - RAILS_ROOT/features/support/env.rb)
|
67
65
|
World(FactoryBot::Syntax::Methods)
|
68
66
|
```
|
69
67
|
|
70
|
-
|
68
|
+
### Spinach
|
71
69
|
|
72
70
|
```ruby
|
73
71
|
class Spinach::FeatureSteps
|
@@ -75,7 +73,7 @@ class Spinach::FeatureSteps
|
|
75
73
|
end
|
76
74
|
```
|
77
75
|
|
78
|
-
|
76
|
+
### Minitest
|
79
77
|
|
80
78
|
```ruby
|
81
79
|
class Minitest::Unit::TestCase
|
@@ -83,7 +81,7 @@ class Minitest::Unit::TestCase
|
|
83
81
|
end
|
84
82
|
```
|
85
83
|
|
86
|
-
|
84
|
+
### Minitest::Spec
|
87
85
|
|
88
86
|
```ruby
|
89
87
|
class Minitest::Spec
|
@@ -91,7 +89,7 @@ class Minitest::Spec
|
|
91
89
|
end
|
92
90
|
```
|
93
91
|
|
94
|
-
|
92
|
+
### minitest-rails
|
95
93
|
|
96
94
|
```ruby
|
97
95
|
class ActiveSupport::TestCase
|
@@ -104,23 +102,42 @@ If you do not include `FactoryBot::Syntax::Methods` in your test suite, then all
|
|
104
102
|
Defining factories
|
105
103
|
------------------
|
106
104
|
|
107
|
-
Each factory has a name and a set of attributes. The name is used to guess the class of the object by default
|
105
|
+
Each factory has a name and a set of attributes. The name is used to guess the class of the object by default:
|
108
106
|
|
109
107
|
```ruby
|
110
108
|
# This will guess the User class
|
111
109
|
FactoryBot.define do
|
112
110
|
factory :user do
|
113
|
-
first_name "John"
|
114
|
-
last_name "Doe"
|
115
|
-
admin false
|
111
|
+
first_name { "John" }
|
112
|
+
last_name { "Doe" }
|
113
|
+
admin { false }
|
116
114
|
end
|
115
|
+
end
|
116
|
+
```
|
117
117
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
118
|
+
It is also possible to explicitly specify the class:
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
# This will use the User class (otherwise Admin would have been guessed)
|
122
|
+
factory :admin, class: User
|
123
|
+
```
|
124
|
+
|
125
|
+
If the constant is not available
|
126
|
+
(if you are using a Rails engine that waits to load models, for example),
|
127
|
+
you can also pass a symbol or string,
|
128
|
+
which factory_bot will constantize later, once you start building objects:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
# It's OK if Doorkeeper::AccessToken isn't loaded yet
|
132
|
+
factory :access_token, class: "Doorkeeper::AccessToken"
|
133
|
+
```
|
134
|
+
|
135
|
+
Because of the block syntax in Ruby, defining attributes as `Hash`es (for
|
136
|
+
serialized/JSON columns, for example) requires two sets of curly brackets:
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
factory :program do
|
140
|
+
configuration { { auto_resolve: false, auto_define: true } }
|
124
141
|
end
|
125
142
|
```
|
126
143
|
|
@@ -170,40 +187,24 @@ user.first_name
|
|
170
187
|
# => "Joe"
|
171
188
|
```
|
172
189
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
Most factory attributes can be added using static values that are evaluated when
|
177
|
-
the factory is defined, but some attributes (such as associations and other
|
178
|
-
attributes that must be dynamically generated) will need values assigned each
|
179
|
-
time an instance is generated. These "dynamic" attributes can be added by passing a
|
180
|
-
block instead of a parameter:
|
181
|
-
|
182
|
-
```ruby
|
183
|
-
factory :user do
|
184
|
-
# ...
|
185
|
-
activation_code { User.generate_activation_code }
|
186
|
-
date_of_birth { 21.years.ago }
|
187
|
-
end
|
188
|
-
```
|
190
|
+
Note that objects created with `build_stubbed` cannot be serialized with
|
191
|
+
`Marshal.dump`, since factory_bot defines singleton methods on these objects.
|
189
192
|
|
190
|
-
|
191
|
-
|
193
|
+
Static Attributes
|
194
|
+
------------------
|
192
195
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
197
|
-
```
|
196
|
+
Static attributes (without a block) are no longer available in factory\_bot 5.
|
197
|
+
You can read more about the decision to remove them in
|
198
|
+
[this blog post](https://robots.thoughtbot.com/deprecating-static-attributes-in-factory_bot-4-11).
|
198
199
|
|
199
200
|
Aliases
|
200
201
|
-------
|
201
|
-
factory_bot allows you to define aliases to existing factories to make them easier to re-use. This could come in handy when, for example, your Post object has an author attribute that actually refers to an instance of a User class. While normally factory_bot can infer the factory name from the association name, in this case it will look for
|
202
|
+
factory_bot allows you to define aliases to existing factories to make them easier to re-use. This could come in handy when, for example, your Post object has an author attribute that actually refers to an instance of a User class. While normally factory_bot can infer the factory name from the association name, in this case it will look for an author factory in vain. So, alias your user factory so it can be used under alias names.
|
202
203
|
|
203
204
|
```ruby
|
204
205
|
factory :user, aliases: [:author, :commenter] do
|
205
|
-
first_name
|
206
|
-
last_name
|
206
|
+
first_name { "John" }
|
207
|
+
last_name { "Doe" }
|
207
208
|
date_of_birth { 18.years.ago }
|
208
209
|
end
|
209
210
|
|
@@ -211,15 +212,15 @@ factory :post do
|
|
211
212
|
author
|
212
213
|
# instead of
|
213
214
|
# association :author, factory: :user
|
214
|
-
title "How to read a book effectively"
|
215
|
-
body
|
215
|
+
title { "How to read a book effectively" }
|
216
|
+
body { "There are five steps involved." }
|
216
217
|
end
|
217
218
|
|
218
219
|
factory :comment do
|
219
220
|
commenter
|
220
221
|
# instead of
|
221
222
|
# association :commenter, factory: :user
|
222
|
-
body "Great article!"
|
223
|
+
body { "Great article!" }
|
223
224
|
end
|
224
225
|
```
|
225
226
|
|
@@ -231,8 +232,8 @@ that is yielded to dynamic attribute blocks:
|
|
231
232
|
|
232
233
|
```ruby
|
233
234
|
factory :user do
|
234
|
-
first_name "Joe"
|
235
|
-
last_name "Blow"
|
235
|
+
first_name { "Joe" }
|
236
|
+
last_name { "Blow" }
|
236
237
|
email { "#{first_name}.#{last_name}@example.com".downcase }
|
237
238
|
end
|
238
239
|
|
@@ -248,11 +249,11 @@ There may be times where your code can be DRYed up by passing in transient attri
|
|
248
249
|
```ruby
|
249
250
|
factory :user do
|
250
251
|
transient do
|
251
|
-
rockstar true
|
252
|
-
upcased
|
252
|
+
rockstar { true }
|
253
|
+
upcased { false }
|
253
254
|
end
|
254
255
|
|
255
|
-
name
|
256
|
+
name { "John Doe#{" - Rockstar" if rockstar}" }
|
256
257
|
email { "#{name.downcase}@example.com" }
|
257
258
|
|
258
259
|
after(:create) do |user, evaluator|
|
@@ -264,8 +265,8 @@ create(:user, upcased: true).name
|
|
264
265
|
#=> "JOHN DOE - ROCKSTAR"
|
265
266
|
```
|
266
267
|
|
267
|
-
|
268
|
-
|
268
|
+
Transient attributes will be ignored within attributes\_for and won't be
|
269
|
+
set on the model,
|
269
270
|
even if the attribute exists or you attempt to override it.
|
270
271
|
|
271
272
|
Within factory_bot's dynamic attributes, you can access transient attributes as
|
@@ -276,7 +277,7 @@ transient attributes from there.
|
|
276
277
|
Method Name / Reserved Word Attributes
|
277
278
|
-------------------------------
|
278
279
|
|
279
|
-
If your attributes conflict with existing methods or reserved words you can define them with `add_attribute`.
|
280
|
+
If your attributes conflict with existing methods or reserved words (all methods in the [DefinitionProxy](https://github.com/thoughtbot/factory_bot/blob/master/lib/factory_bot/definition_proxy.rb) class) you can define them with `add_attribute`.
|
280
281
|
|
281
282
|
```ruby
|
282
283
|
factory :dna do
|
@@ -296,10 +297,10 @@ You can easily create multiple factories for the same class without repeating co
|
|
296
297
|
|
297
298
|
```ruby
|
298
299
|
factory :post do
|
299
|
-
title "A title"
|
300
|
+
title { "A title" }
|
300
301
|
|
301
302
|
factory :approved_post do
|
302
|
-
approved true
|
303
|
+
approved { true }
|
303
304
|
end
|
304
305
|
end
|
305
306
|
|
@@ -312,11 +313,11 @@ You can also assign the parent explicitly:
|
|
312
313
|
|
313
314
|
```ruby
|
314
315
|
factory :post do
|
315
|
-
title "A title"
|
316
|
+
title { "A title" }
|
316
317
|
end
|
317
318
|
|
318
319
|
factory :approved_post, parent: :post do
|
319
|
-
approved true
|
320
|
+
approved { true }
|
320
321
|
end
|
321
322
|
```
|
322
323
|
|
@@ -346,9 +347,35 @@ factory :post do
|
|
346
347
|
end
|
347
348
|
```
|
348
349
|
|
349
|
-
|
350
|
+
In factory\_bot 5, associations default to using the same build strategy as
|
351
|
+
their parent object:
|
352
|
+
|
353
|
+
```ruby
|
354
|
+
FactoryBot.define do
|
355
|
+
factory :author
|
356
|
+
|
357
|
+
factory :post do
|
358
|
+
author
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
post = build(:post)
|
363
|
+
post.new_record? # => true
|
364
|
+
post.author.new_record? # => true
|
365
|
+
|
366
|
+
post = create(:post)
|
367
|
+
post.new_record? # => false
|
368
|
+
post.author.new_record? # => false
|
369
|
+
```
|
370
|
+
|
371
|
+
This is different than the default behavior for previous versions of
|
372
|
+
factory\_bot, where the association strategy would not always match the strategy
|
373
|
+
of the parent object. If you want to continue using the old behavior, you can
|
374
|
+
set the `use_parent_strategy` configuration option to `false`.
|
350
375
|
|
351
376
|
```ruby
|
377
|
+
FactoryBot.use_parent_strategy = false
|
378
|
+
|
352
379
|
# Builds and saves a User and a Post
|
353
380
|
post = create(:post)
|
354
381
|
post.new_record? # => false
|
@@ -360,9 +387,11 @@ post.new_record? # => true
|
|
360
387
|
post.author.new_record? # => false
|
361
388
|
```
|
362
389
|
|
363
|
-
To not save the associated object, specify strategy: :build in the factory:
|
390
|
+
To not save the associated object, specify `strategy: :build` in the factory:
|
364
391
|
|
365
392
|
```ruby
|
393
|
+
FactoryBot.use_parent_strategy = false
|
394
|
+
|
366
395
|
factory :post do
|
367
396
|
# ...
|
368
397
|
association :author, factory: :user, strategy: :build
|
@@ -392,20 +421,20 @@ FactoryBot.define do
|
|
392
421
|
|
393
422
|
# post factory with a `belongs_to` association for the user
|
394
423
|
factory :post do
|
395
|
-
title "Through the Looking Glass"
|
424
|
+
title { "Through the Looking Glass" }
|
396
425
|
user
|
397
426
|
end
|
398
427
|
|
399
428
|
# user factory without associated posts
|
400
429
|
factory :user do
|
401
|
-
name "John Doe"
|
430
|
+
name { "John Doe" }
|
402
431
|
|
403
432
|
# user_with_posts will create post data after the user has been created
|
404
433
|
factory :user_with_posts do
|
405
434
|
# posts_count is declared as a transient attribute and available in
|
406
435
|
# attributes on the factory, as well as the callback via the evaluator
|
407
436
|
transient do
|
408
|
-
posts_count 5
|
437
|
+
posts_count { 5 }
|
409
438
|
end
|
410
439
|
|
411
440
|
# the after(:create) yields two values; the user instance itself and the
|
@@ -441,13 +470,13 @@ FactoryBot.define do
|
|
441
470
|
|
442
471
|
# language factory with a `belongs_to` association for the profile
|
443
472
|
factory :language do
|
444
|
-
title "Through the Looking Glass"
|
473
|
+
title { "Through the Looking Glass" }
|
445
474
|
profile
|
446
475
|
end
|
447
476
|
|
448
477
|
# profile factory without associated languages
|
449
478
|
factory :profile do
|
450
|
-
name "John Doe"
|
479
|
+
name { "John Doe" }
|
451
480
|
|
452
481
|
# profile_with_languages will create language data after the profile has
|
453
482
|
# been created
|
@@ -455,7 +484,7 @@ FactoryBot.define do
|
|
455
484
|
# languages_count is declared as an ignored attribute and available in
|
456
485
|
# attributes on the factory, as well as the callback via the evaluator
|
457
486
|
transient do
|
458
|
-
languages_count 5
|
487
|
+
languages_count { 5 }
|
459
488
|
end
|
460
489
|
|
461
490
|
# the after(:create) yields two values; the profile instance itself and
|
@@ -479,6 +508,36 @@ create(:profile_with_languages).languages.length # 5
|
|
479
508
|
create(:profile_with_languages, languages_count: 15).languages.length # 15
|
480
509
|
```
|
481
510
|
|
511
|
+
Polymorphic associations can be handled with traits:
|
512
|
+
|
513
|
+
```ruby
|
514
|
+
FactoryBot.define do
|
515
|
+
factory :video
|
516
|
+
factory :photo
|
517
|
+
|
518
|
+
factory :comment do
|
519
|
+
for_photo # default to the :for_photo trait if none is specified
|
520
|
+
|
521
|
+
trait :for_video do
|
522
|
+
association :commentable, factory: :video
|
523
|
+
end
|
524
|
+
|
525
|
+
trait :for_photo do
|
526
|
+
association :commentable, factory: :photo
|
527
|
+
end
|
528
|
+
end
|
529
|
+
end
|
530
|
+
```
|
531
|
+
|
532
|
+
This allows us to do:
|
533
|
+
|
534
|
+
```ruby
|
535
|
+
create(:comment)
|
536
|
+
create(:comment, :for_video)
|
537
|
+
create(:comment, :for_photo)
|
538
|
+
```
|
539
|
+
|
540
|
+
|
482
541
|
Sequences
|
483
542
|
---------
|
484
543
|
|
@@ -518,6 +577,9 @@ factory :user do
|
|
518
577
|
end
|
519
578
|
```
|
520
579
|
|
580
|
+
Note that defining sequences as implicit attributes will not work if you have a
|
581
|
+
factory with the same name as the sequence.
|
582
|
+
|
521
583
|
And it's also possible to define an in-line sequence that is only used in
|
522
584
|
a particular factory:
|
523
585
|
|
@@ -572,6 +634,22 @@ end
|
|
572
634
|
|
573
635
|
The value just needs to support the `#next` method. Here the next value will be 'a', then 'b', etc.
|
574
636
|
|
637
|
+
Sequences can also be rewound with `FactoryBot.rewind_sequences`:
|
638
|
+
|
639
|
+
```ruby
|
640
|
+
sequence(:email) {|n| "person#{n}@example.com" }
|
641
|
+
|
642
|
+
generate(:email) # "person1@example.com"
|
643
|
+
generate(:email) # "person2@example.com"
|
644
|
+
generate(:email) # "person3@example.com"
|
645
|
+
|
646
|
+
FactoryBot.rewind_sequences
|
647
|
+
|
648
|
+
generate(:email) # "person1@example.com"
|
649
|
+
```
|
650
|
+
|
651
|
+
This rewinds all registered sequences.
|
652
|
+
|
575
653
|
Traits
|
576
654
|
------
|
577
655
|
|
@@ -582,25 +660,25 @@ to any factory.
|
|
582
660
|
factory :user, aliases: [:author]
|
583
661
|
|
584
662
|
factory :story do
|
585
|
-
title "My awesome story"
|
663
|
+
title { "My awesome story" }
|
586
664
|
author
|
587
665
|
|
588
666
|
trait :published do
|
589
|
-
published true
|
667
|
+
published { true }
|
590
668
|
end
|
591
669
|
|
592
670
|
trait :unpublished do
|
593
|
-
published false
|
671
|
+
published { false }
|
594
672
|
end
|
595
673
|
|
596
674
|
trait :week_long_publishing do
|
597
675
|
start_at { 1.week.ago }
|
598
|
-
end_at
|
676
|
+
end_at { Time.now }
|
599
677
|
end
|
600
678
|
|
601
679
|
trait :month_long_publishing do
|
602
680
|
start_at { 1.month.ago }
|
603
|
-
end_at
|
681
|
+
end_at { Time.now }
|
604
682
|
end
|
605
683
|
|
606
684
|
factory :week_long_published_story, traits: [:published, :week_long_publishing]
|
@@ -610,7 +688,7 @@ factory :story do
|
|
610
688
|
end
|
611
689
|
```
|
612
690
|
|
613
|
-
Traits can be used as attributes:
|
691
|
+
Traits can be used as implicit attributes:
|
614
692
|
|
615
693
|
```ruby
|
616
694
|
factory :week_long_published_story_with_title, parent: :story do
|
@@ -620,28 +698,31 @@ factory :week_long_published_story_with_title, parent: :story do
|
|
620
698
|
end
|
621
699
|
```
|
622
700
|
|
701
|
+
Note that defining traits as implicit attributes will not work if you have a
|
702
|
+
factory or sequence with the same name as the trait.
|
703
|
+
|
623
704
|
Traits that define the same attributes won't raise AttributeDefinitionErrors;
|
624
705
|
the trait that defines the attribute latest gets precedence.
|
625
706
|
|
626
707
|
```ruby
|
627
708
|
factory :user do
|
628
|
-
name "Friendly User"
|
709
|
+
name { "Friendly User" }
|
629
710
|
login { name }
|
630
711
|
|
631
712
|
trait :male do
|
632
|
-
name
|
633
|
-
gender "Male"
|
713
|
+
name { "John Doe" }
|
714
|
+
gender { "Male" }
|
634
715
|
login { "#{name} (M)" }
|
635
716
|
end
|
636
717
|
|
637
718
|
trait :female do
|
638
|
-
name
|
639
|
-
gender "Female"
|
719
|
+
name { "Jane Doe" }
|
720
|
+
gender { "Female" }
|
640
721
|
login { "#{name} (F)" }
|
641
722
|
end
|
642
723
|
|
643
724
|
trait :admin do
|
644
|
-
admin true
|
725
|
+
admin { true }
|
645
726
|
login { "admin-#{name}" }
|
646
727
|
end
|
647
728
|
|
@@ -654,18 +735,18 @@ You can also override individual attributes granted by a trait in subclasses.
|
|
654
735
|
|
655
736
|
```ruby
|
656
737
|
factory :user do
|
657
|
-
name "Friendly User"
|
738
|
+
name { "Friendly User" }
|
658
739
|
login { name }
|
659
740
|
|
660
741
|
trait :male do
|
661
|
-
name
|
662
|
-
gender "Male"
|
742
|
+
name { "John Doe" }
|
743
|
+
gender { "Male" }
|
663
744
|
login { "#{name} (M)" }
|
664
745
|
end
|
665
746
|
|
666
747
|
factory :brandon do
|
667
748
|
male
|
668
|
-
name "Brandon"
|
749
|
+
name { "Brandon" }
|
669
750
|
end
|
670
751
|
end
|
671
752
|
```
|
@@ -674,15 +755,15 @@ Traits can also be passed in as a list of symbols when you construct an instance
|
|
674
755
|
|
675
756
|
```ruby
|
676
757
|
factory :user do
|
677
|
-
name "Friendly User"
|
758
|
+
name { "Friendly User" }
|
678
759
|
|
679
760
|
trait :male do
|
680
|
-
name
|
681
|
-
gender "Male"
|
761
|
+
name { "John Doe" }
|
762
|
+
gender { "Male" }
|
682
763
|
end
|
683
764
|
|
684
765
|
trait :admin do
|
685
|
-
admin true
|
766
|
+
admin { true }
|
686
767
|
end
|
687
768
|
end
|
688
769
|
|
@@ -698,10 +779,10 @@ the number of instances to create/build as second parameter, as documented in th
|
|
698
779
|
|
699
780
|
```ruby
|
700
781
|
factory :user do
|
701
|
-
name "Friendly User"
|
782
|
+
name { "Friendly User" }
|
702
783
|
|
703
784
|
trait :admin do
|
704
|
-
admin true
|
785
|
+
admin { true }
|
705
786
|
end
|
706
787
|
end
|
707
788
|
|
@@ -713,10 +794,10 @@ Traits can be used with associations easily too:
|
|
713
794
|
|
714
795
|
```ruby
|
715
796
|
factory :user do
|
716
|
-
name "Friendly User"
|
797
|
+
name { "Friendly User" }
|
717
798
|
|
718
799
|
trait :admin do
|
719
|
-
admin true
|
800
|
+
admin { true }
|
720
801
|
end
|
721
802
|
end
|
722
803
|
|
@@ -732,10 +813,10 @@ When you're using association names that're different than the factory:
|
|
732
813
|
|
733
814
|
```ruby
|
734
815
|
factory :user do
|
735
|
-
name "Friendly User"
|
816
|
+
name { "Friendly User" }
|
736
817
|
|
737
818
|
trait :admin do
|
738
|
-
admin true
|
819
|
+
admin { true }
|
739
820
|
end
|
740
821
|
end
|
741
822
|
|
@@ -770,7 +851,7 @@ Finally, traits can accept transient attributes.
|
|
770
851
|
factory :invoice do
|
771
852
|
trait :with_amount do
|
772
853
|
transient do
|
773
|
-
amount 1
|
854
|
+
amount { 1 }
|
774
855
|
end
|
775
856
|
|
776
857
|
after(:create) do |invoice, evaluator|
|
@@ -844,7 +925,7 @@ FactoryBot.define do
|
|
844
925
|
after(:create) { |object| AuditLog.create(attrs: object.attributes) }
|
845
926
|
|
846
927
|
factory :user do
|
847
|
-
name "John Doe"
|
928
|
+
name { "John Doe" }
|
848
929
|
end
|
849
930
|
end
|
850
931
|
```
|
@@ -880,9 +961,9 @@ If a gem were to give you a User factory:
|
|
880
961
|
```ruby
|
881
962
|
FactoryBot.define do
|
882
963
|
factory :user do
|
883
|
-
full_name "John Doe"
|
964
|
+
full_name { "John Doe" }
|
884
965
|
sequence(:username) { |n| "user#{n}" }
|
885
|
-
password "password"
|
966
|
+
password { "password" }
|
886
967
|
end
|
887
968
|
end
|
888
969
|
```
|
@@ -892,10 +973,10 @@ Instead of creating a child factory that added additional attributes:
|
|
892
973
|
```ruby
|
893
974
|
FactoryBot.define do
|
894
975
|
factory :application_user, parent: :user do
|
895
|
-
full_name
|
976
|
+
full_name { "Jane Doe" }
|
896
977
|
date_of_birth { 21.years.ago }
|
897
|
-
gender
|
898
|
-
health
|
978
|
+
gender { "Female" }
|
979
|
+
health { 90 }
|
899
980
|
end
|
900
981
|
end
|
901
982
|
```
|
@@ -905,10 +986,10 @@ You could modify that factory instead.
|
|
905
986
|
```ruby
|
906
987
|
FactoryBot.modify do
|
907
988
|
factory :user do
|
908
|
-
full_name
|
989
|
+
full_name { "Jane Doe" }
|
909
990
|
date_of_birth { 21.years.ago }
|
910
|
-
gender
|
911
|
-
health
|
991
|
+
gender { "Female" }
|
992
|
+
health { 90 }
|
912
993
|
end
|
913
994
|
end
|
914
995
|
```
|
@@ -937,6 +1018,14 @@ To set the attributes for each of the factories, you can pass in a hash as you n
|
|
937
1018
|
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
|
938
1019
|
```
|
939
1020
|
|
1021
|
+
In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters:
|
1022
|
+
|
1023
|
+
```ruby
|
1024
|
+
twenty_somethings = build_list(:user, 10) do |user, i|
|
1025
|
+
user.date_of_birth = (20 + i).years.ago
|
1026
|
+
end
|
1027
|
+
```
|
1028
|
+
|
940
1029
|
`build_stubbed_list` will give you fully stubbed out instances:
|
941
1030
|
|
942
1031
|
```ruby
|
@@ -986,12 +1075,14 @@ namespace :factory_bot do
|
|
986
1075
|
desc "Verify that all FactoryBot factories are valid"
|
987
1076
|
task lint: :environment do
|
988
1077
|
if Rails.env.test?
|
989
|
-
|
1078
|
+
conn = ActiveRecord::Base.connection
|
1079
|
+
conn.transaction do
|
990
1080
|
FactoryBot.lint
|
1081
|
+
raise ActiveRecord::Rollback
|
991
1082
|
end
|
992
1083
|
else
|
993
1084
|
system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
|
994
|
-
|
1085
|
+
fail if $?.exitstatus.nonzero?
|
995
1086
|
end
|
996
1087
|
end
|
997
1088
|
end
|
@@ -999,8 +1090,7 @@ end
|
|
999
1090
|
|
1000
1091
|
After calling `FactoryBot.lint`, you'll likely want to clear out the
|
1001
1092
|
database, as records will most likely be created. The provided example above
|
1002
|
-
uses
|
1003
|
-
gem to your Gemfile under the appropriate groups.
|
1093
|
+
uses an sql transaction and rollback to leave the database clean.
|
1004
1094
|
|
1005
1095
|
You can lint factories selectively by passing only factories you want linted:
|
1006
1096
|
|
@@ -1034,6 +1124,13 @@ You can also specify the strategy used for linting:
|
|
1034
1124
|
FactoryBot.lint strategy: :build
|
1035
1125
|
```
|
1036
1126
|
|
1127
|
+
Verbose linting will include full backtraces for each error, which can be
|
1128
|
+
helpful for debugging:
|
1129
|
+
|
1130
|
+
```ruby
|
1131
|
+
FactoryBot.lint verbose: true
|
1132
|
+
```
|
1133
|
+
|
1037
1134
|
Custom Construction
|
1038
1135
|
-------------------
|
1039
1136
|
|
@@ -1056,7 +1153,7 @@ end
|
|
1056
1153
|
sequence(:email) { |n| "person#{n}@example.com" }
|
1057
1154
|
|
1058
1155
|
factory :user do
|
1059
|
-
name "Jane Doe"
|
1156
|
+
name { "Jane Doe" }
|
1060
1157
|
email
|
1061
1158
|
|
1062
1159
|
initialize_with { new(name) }
|
@@ -1086,7 +1183,7 @@ For example:
|
|
1086
1183
|
|
1087
1184
|
```ruby
|
1088
1185
|
factory :user do
|
1089
|
-
name "John Doe"
|
1186
|
+
name { "John Doe" }
|
1090
1187
|
|
1091
1188
|
initialize_with { User.build_with_name(name) }
|
1092
1189
|
end
|
@@ -1098,7 +1195,7 @@ by calling `attributes`:
|
|
1098
1195
|
```ruby
|
1099
1196
|
factory :user do
|
1100
1197
|
transient do
|
1101
|
-
comments_count 5
|
1198
|
+
comments_count { 5 }
|
1102
1199
|
end
|
1103
1200
|
|
1104
1201
|
name "John Doe"
|
@@ -1109,7 +1206,7 @@ end
|
|
1109
1206
|
|
1110
1207
|
This will build a hash of all attributes to be passed to `new`. It won't
|
1111
1208
|
include transient attributes, but everything else defined in the factory will be
|
1112
|
-
passed (associations,
|
1209
|
+
passed (associations, evaluated sequences, etc.)
|
1113
1210
|
|
1114
1211
|
You can define `initialize_with` for all factories by including it in the
|
1115
1212
|
`FactoryBot.define` block:
|
@@ -1271,7 +1368,7 @@ FactoryBot.define do
|
|
1271
1368
|
|
1272
1369
|
|
1273
1370
|
factory :user do
|
1274
|
-
name "John Doe"
|
1371
|
+
name { "John Doe" }
|
1275
1372
|
end
|
1276
1373
|
end
|
1277
1374
|
```
|
@@ -1324,13 +1421,13 @@ with associations, as below:
|
|
1324
1421
|
|
1325
1422
|
```ruby
|
1326
1423
|
FactoryBot.define do
|
1327
|
-
factory :united_states, class: Location do
|
1328
|
-
name 'United States'
|
1424
|
+
factory :united_states, class: "Location" do
|
1425
|
+
name { 'United States' }
|
1329
1426
|
association :location_group, factory: :north_america
|
1330
1427
|
end
|
1331
1428
|
|
1332
|
-
factory :north_america, class: LocationGroup do
|
1333
|
-
name 'North America'
|
1429
|
+
factory :north_america, class: "LocationGroup" do
|
1430
|
+
name { 'North America' }
|
1334
1431
|
end
|
1335
1432
|
end
|
1336
1433
|
```
|
@@ -1362,7 +1459,7 @@ require 'factory_bot'
|
|
1362
1459
|
```
|
1363
1460
|
|
1364
1461
|
Once required, assuming you have a directory structure of `spec/factories` or
|
1365
|
-
`test/factories`, all you'll need to do is run
|
1462
|
+
`test/factories`, all you'll need to do is run:
|
1366
1463
|
|
1367
1464
|
```ruby
|
1368
1465
|
FactoryBot.find_definitions
|
@@ -1384,7 +1481,7 @@ require 'factory_bot'
|
|
1384
1481
|
|
1385
1482
|
FactoryBot.define do
|
1386
1483
|
factory :user do
|
1387
|
-
name 'John Doe'
|
1484
|
+
name { 'John Doe' }
|
1388
1485
|
date_of_birth { 21.years.ago }
|
1389
1486
|
end
|
1390
1487
|
end
|