factory_bot 4.11.1 → 6.2.1
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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +58 -13
- data/GETTING_STARTED.md +825 -153
- data/LICENSE +1 -1
- data/NEWS.md +385 -0
- data/README.md +20 -30
- data/lib/factory_bot/aliases.rb +3 -3
- data/lib/factory_bot/attribute/association.rb +2 -2
- data/lib/factory_bot/attribute/dynamic.rb +3 -2
- data/lib/factory_bot/attribute.rb +4 -39
- data/lib/factory_bot/attribute_assigner.rb +27 -12
- data/lib/factory_bot/attribute_list.rb +3 -2
- data/lib/factory_bot/callback.rb +4 -11
- data/lib/factory_bot/configuration.rb +15 -19
- data/lib/factory_bot/declaration/association.rb +33 -3
- data/lib/factory_bot/declaration/dynamic.rb +3 -1
- data/lib/factory_bot/declaration/implicit.rb +7 -2
- data/lib/factory_bot/declaration.rb +5 -5
- data/lib/factory_bot/declaration_list.rb +3 -3
- data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +2 -1
- data/lib/factory_bot/decorator.rb +20 -4
- data/lib/factory_bot/definition.rb +69 -21
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +119 -64
- data/lib/factory_bot/enum.rb +27 -0
- data/lib/factory_bot/errors.rb +7 -4
- data/lib/factory_bot/evaluation.rb +1 -1
- data/lib/factory_bot/evaluator.rb +10 -11
- data/lib/factory_bot/evaluator_class_definer.rb +1 -1
- data/lib/factory_bot/factory.rb +12 -12
- data/lib/factory_bot/factory_runner.rb +4 -4
- data/lib/factory_bot/find_definitions.rb +2 -2
- data/lib/factory_bot/internal.rb +91 -0
- data/lib/factory_bot/linter.rb +41 -28
- data/lib/factory_bot/null_factory.rb +13 -4
- data/lib/factory_bot/null_object.rb +2 -6
- data/lib/factory_bot/registry.rb +17 -8
- data/lib/factory_bot/reload.rb +2 -3
- data/lib/factory_bot/sequence.rb +5 -6
- data/lib/factory_bot/strategy/attributes_for.rb +4 -0
- data/lib/factory_bot/strategy/build.rb +4 -0
- data/lib/factory_bot/strategy/create.rb +4 -0
- data/lib/factory_bot/strategy/null.rb +4 -0
- data/lib/factory_bot/strategy/stub.rb +41 -32
- 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 +13 -25
- data/lib/factory_bot/syntax/methods.rb +32 -9
- data/lib/factory_bot/syntax.rb +2 -2
- data/lib/factory_bot/trait.rb +7 -4
- data/lib/factory_bot/version.rb +1 -1
- data/lib/factory_bot.rb +71 -140
- metadata +46 -34
- data/NEWS +0 -306
- 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/GETTING_STARTED.md
CHANGED
@@ -1,36 +1,111 @@
|
|
1
1
|
Getting Started
|
2
2
|
===============
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
* [Setup](#setup)
|
5
|
+
+ [Update Your Gemfile](#update-your-gemfile)
|
6
|
+
+ [Configure your test suite](#configure-your-test-suite)
|
7
|
+
- [RSpec](#rspec)
|
8
|
+
- [Test::Unit](#testunit)
|
9
|
+
- [Cucumber](#cucumber)
|
10
|
+
- [Spinach](#spinach)
|
11
|
+
- [Minitest](#minitest)
|
12
|
+
- [Minitest::Spec](#minitestspec)
|
13
|
+
- [minitest-rails](#minitest-rails)
|
14
|
+
* [Defining factories](#defining-factories)
|
15
|
+
+ [Factory name and attributes](#factory-name-and-attributes)
|
16
|
+
+ [Specifying the class explicitly](#specifying-the-class-explicitly)
|
17
|
+
+ [Hash attributes](#hash-attributes)
|
18
|
+
+ [Best practices](#best-practices)
|
19
|
+
+ [Definition file paths](#definition-file-paths)
|
20
|
+
+ [Static Attributes](#static-attributes)
|
21
|
+
* [Using factories](#using-factories)
|
22
|
+
+ [Build strategies](#build-strategies)
|
23
|
+
+ [Attribute overrides](#attribute-overrides)
|
24
|
+
+ [`build_stubbed` and `Marshal.dump`](#build_stubbed-and-marshaldump)
|
25
|
+
* [Aliases](#aliases)
|
26
|
+
* [Dependent Attributes](#dependent-attributes)
|
27
|
+
* [Transient Attributes](#transient-attributes)
|
28
|
+
+ [With other attributes](#with-other-attributes)
|
29
|
+
+ [With `attributes_for`](#with-attributes_for)
|
30
|
+
+ [With callbacks](#with-callbacks)
|
31
|
+
+ [With associations](#with-associations)
|
32
|
+
* [Method Name / Reserved Word Attributes](#method-name--reserved-word-attributes)
|
33
|
+
* [Inheritance](#inheritance)
|
34
|
+
+ [Nested factories](#nested-factories)
|
35
|
+
+ [Assigning parent explicitly](#assigning-parent-explicitly)
|
36
|
+
+ [Best practices](#best-practices-1)
|
37
|
+
* [Associations](#associations)
|
38
|
+
+ [Implicit definition](#implicit-definition)
|
39
|
+
+ [Explicit definition](#explicit-definition)
|
40
|
+
+ [Inline definition](#inline-definition)
|
41
|
+
+ [Specifying the factory](#specifying-the-factory)
|
42
|
+
+ [Overriding attributes](#overriding-attributes)
|
43
|
+
+ [Association overrides](#association-overrides)
|
44
|
+
+ [Build strategies](#build-strategies-1)
|
45
|
+
+ [`has_many` associations](#has_many-associations)
|
46
|
+
+ [`has_and_belongs_to_many` associations](#has_and_belongs_to_many-associations)
|
47
|
+
+ [Polymorphic associations](#polymorphic-associations)
|
48
|
+
+ [Interconnected associations](#interconnected-associations)
|
49
|
+
* [Sequences](#sequences)
|
50
|
+
+ [Global sequences](#global-sequences)
|
51
|
+
+ [With dynamic attributes](#with-dynamic-attributes)
|
52
|
+
+ [As implicit attributes](#as-implicit-attributes)
|
53
|
+
+ [Inline sequences](#inline-sequences)
|
54
|
+
+ [Initial value](#initial-value)
|
55
|
+
+ [Without a block](#without-a-block)
|
56
|
+
+ [Aliases](#aliases-1)
|
57
|
+
+ [Rewinding](#rewinding)
|
58
|
+
+ [Uniqueness](#uniqueness)
|
59
|
+
* [Traits](#traits)
|
60
|
+
+ [Defining traits](#defining-traits)
|
61
|
+
+ [As implicit attributes](#as-implicit-attributes-1)
|
62
|
+
+ [Attribute precedence](#attribute-precedence)
|
63
|
+
+ [In child factories](#in-child-factories)
|
64
|
+
+ [Using traits](#using-traits)
|
65
|
+
+ [With associations](#with-associations-1)
|
66
|
+
+ [Traits within traits](#traits-within-traits)
|
67
|
+
+ [With transient attributes](#with-transient-attributes)
|
68
|
+
+ [Enum traits](#enum-traits)
|
69
|
+
* [Callbacks](#callbacks)
|
70
|
+
+ [Default callbacks](#default-callbacks)
|
71
|
+
+ [Multiple callbacks](#multiple-callbacks)
|
72
|
+
+ [Global callbacks](#global-callbacks)
|
73
|
+
+ [Symbol#to_proc](#symbolto_proc)
|
74
|
+
* [Modifying factories](#modifying-factories)
|
75
|
+
* [Building or Creating Multiple Records](#building-or-creating-multiple-records)
|
76
|
+
* [Linting Factories](#linting-factories)
|
77
|
+
* [Custom Construction](#custom-construction)
|
78
|
+
* [Custom Strategies](#custom-strategies)
|
79
|
+
* [Custom Callbacks](#custom-callbacks)
|
80
|
+
* [Custom Methods to Persist Objects](#custom-methods-to-persist-objects)
|
81
|
+
* [ActiveSupport Instrumentation](#activesupport-instrumentation)
|
82
|
+
* [Rails Preloaders and RSpec](#rails-preloaders-and-rspec)
|
83
|
+
* [Using Without Bundler](#using-without-bundler)
|
84
|
+
|
85
|
+
Setup
|
86
|
+
-----
|
87
|
+
|
88
|
+
### Update Your Gemfile
|
6
89
|
|
7
|
-
If you're using Rails
|
90
|
+
If you're using Rails:
|
8
91
|
|
9
92
|
```ruby
|
10
|
-
gem "factory_bot_rails"
|
93
|
+
gem "factory_bot_rails"
|
11
94
|
```
|
12
95
|
|
13
|
-
If you're *not* using Rails
|
96
|
+
If you're *not* using Rails:
|
14
97
|
|
15
98
|
```ruby
|
16
|
-
gem "factory_bot"
|
99
|
+
gem "factory_bot"
|
17
100
|
```
|
18
101
|
|
19
|
-
|
20
|
-
JRuby has to be used in 1.9 mode, for that, use JRUBY_OPTS environment variable:
|
102
|
+
### Configure your test suite
|
21
103
|
|
22
|
-
|
23
|
-
export JRUBY_OPTS=--1.9
|
24
|
-
```
|
104
|
+
#### RSpec
|
25
105
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
-------------------------
|
30
|
-
|
31
|
-
### RSpec
|
32
|
-
|
33
|
-
If you're using Rails:
|
106
|
+
If you're using Rails, add the following configuration to
|
107
|
+
`spec/support/factory_bot.rb` and be sure to require that file in
|
108
|
+
`rails_helper.rb`:
|
34
109
|
|
35
110
|
```ruby
|
36
111
|
RSpec.configure do |config|
|
@@ -50,7 +125,7 @@ RSpec.configure do |config|
|
|
50
125
|
end
|
51
126
|
```
|
52
127
|
|
53
|
-
|
128
|
+
#### Test::Unit
|
54
129
|
|
55
130
|
```ruby
|
56
131
|
class Test::Unit::TestCase
|
@@ -58,14 +133,14 @@ class Test::Unit::TestCase
|
|
58
133
|
end
|
59
134
|
```
|
60
135
|
|
61
|
-
|
136
|
+
#### Cucumber
|
62
137
|
|
63
138
|
```ruby
|
64
139
|
# env.rb (Rails example location - RAILS_ROOT/features/support/env.rb)
|
65
140
|
World(FactoryBot::Syntax::Methods)
|
66
141
|
```
|
67
142
|
|
68
|
-
|
143
|
+
#### Spinach
|
69
144
|
|
70
145
|
```ruby
|
71
146
|
class Spinach::FeatureSteps
|
@@ -73,7 +148,7 @@ class Spinach::FeatureSteps
|
|
73
148
|
end
|
74
149
|
```
|
75
150
|
|
76
|
-
|
151
|
+
#### Minitest
|
77
152
|
|
78
153
|
```ruby
|
79
154
|
class Minitest::Unit::TestCase
|
@@ -81,7 +156,7 @@ class Minitest::Unit::TestCase
|
|
81
156
|
end
|
82
157
|
```
|
83
158
|
|
84
|
-
|
159
|
+
#### Minitest::Spec
|
85
160
|
|
86
161
|
```ruby
|
87
162
|
class Minitest::Spec
|
@@ -89,7 +164,7 @@ class Minitest::Spec
|
|
89
164
|
end
|
90
165
|
```
|
91
166
|
|
92
|
-
|
167
|
+
#### minitest-rails
|
93
168
|
|
94
169
|
```ruby
|
95
170
|
class ActiveSupport::TestCase
|
@@ -97,12 +172,16 @@ class ActiveSupport::TestCase
|
|
97
172
|
end
|
98
173
|
```
|
99
174
|
|
100
|
-
If you do not include `FactoryBot::Syntax::Methods` in your test suite, then all
|
175
|
+
If you do not include `FactoryBot::Syntax::Methods` in your test suite, then all
|
176
|
+
factory\_bot methods will need to be prefaced with `FactoryBot`.
|
101
177
|
|
102
178
|
Defining factories
|
103
179
|
------------------
|
104
180
|
|
105
|
-
|
181
|
+
### Factory name and attributes
|
182
|
+
|
183
|
+
Each factory has a name and a set of attributes. The name is used to guess the
|
184
|
+
class of the object by default:
|
106
185
|
|
107
186
|
```ruby
|
108
187
|
# This will guess the User class
|
@@ -112,16 +191,28 @@ FactoryBot.define do
|
|
112
191
|
last_name { "Doe" }
|
113
192
|
admin { false }
|
114
193
|
end
|
115
|
-
|
116
|
-
# This will use the User class (Admin would have been guessed)
|
117
|
-
factory :admin, class: User do
|
118
|
-
first_name { "Admin" }
|
119
|
-
last_name { "User" }
|
120
|
-
admin { true }
|
121
|
-
end
|
122
194
|
end
|
123
195
|
```
|
124
196
|
|
197
|
+
### Specifying the class explicitly
|
198
|
+
|
199
|
+
It is also possible to explicitly specify the class:
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
# This will use the User class (otherwise Admin would have been guessed)
|
203
|
+
factory :admin, class: "User"
|
204
|
+
```
|
205
|
+
|
206
|
+
You can pass a constant as well, if the constant is available (note that this
|
207
|
+
can cause test performance problems in large Rails applications, since
|
208
|
+
referring to the constant will cause it to be eagerly loaded).
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
factory :access_token, class: User
|
212
|
+
```
|
213
|
+
|
214
|
+
### Hash attributes
|
215
|
+
|
125
216
|
Because of the block syntax in Ruby, defining attributes as `Hash`es (for
|
126
217
|
serialized/JSON columns, for example) requires two sets of curly brackets:
|
127
218
|
|
@@ -131,10 +222,19 @@ factory :program do
|
|
131
222
|
end
|
132
223
|
```
|
133
224
|
|
134
|
-
|
225
|
+
### Best practices
|
226
|
+
|
227
|
+
It is recommended that you have one factory for each class that provides
|
228
|
+
the simplest set of attributes necessary to create an instance of that class. If
|
229
|
+
you're creating ActiveRecord objects, that means that you should only provide
|
230
|
+
attributes that are required through validations and that do not have defaults.
|
231
|
+
Other factories can be created through inheritance to cover common scenarios for
|
232
|
+
each class.
|
135
233
|
|
136
234
|
Attempting to define multiple factories with the same name will raise an error.
|
137
235
|
|
236
|
+
### Definition file paths
|
237
|
+
|
138
238
|
Factories can be defined anywhere, but will be automatically loaded after
|
139
239
|
calling `FactoryBot.find_definitions` if factories are defined in files at the
|
140
240
|
following locations:
|
@@ -144,10 +244,20 @@ following locations:
|
|
144
244
|
test/factories/*.rb
|
145
245
|
spec/factories/*.rb
|
146
246
|
|
247
|
+
### Static Attributes
|
248
|
+
|
249
|
+
Static attributes (without a block) are no longer available in factory\_bot 5.
|
250
|
+
You can read more about the decision to remove them in
|
251
|
+
[this blog post](https://robots.thoughtbot.com/deprecating-static-attributes-in-factory_bot-4-11).
|
252
|
+
|
253
|
+
|
147
254
|
Using factories
|
148
255
|
---------------
|
149
256
|
|
150
|
-
|
257
|
+
### Build strategies
|
258
|
+
|
259
|
+
factory\_bot supports several different build strategies: build, create,
|
260
|
+
attributes\_for and build\_stubbed:
|
151
261
|
|
152
262
|
```ruby
|
153
263
|
# Returns a User instance that's not saved
|
@@ -168,7 +278,10 @@ create(:user) do |user|
|
|
168
278
|
end
|
169
279
|
```
|
170
280
|
|
171
|
-
|
281
|
+
### Attribute overrides
|
282
|
+
|
283
|
+
No matter which strategy is used, it's possible to override the defined
|
284
|
+
attributes by passing a hash:
|
172
285
|
|
173
286
|
```ruby
|
174
287
|
# Build a User instance and override the first_name property
|
@@ -177,25 +290,20 @@ user.first_name
|
|
177
290
|
# => "Joe"
|
178
291
|
```
|
179
292
|
|
180
|
-
|
181
|
-
------------------
|
293
|
+
### `build_stubbed` and `Marshal.dump`
|
182
294
|
|
183
|
-
|
184
|
-
factory\_bot
|
185
|
-
|
186
|
-
```ruby
|
187
|
-
factory :user do
|
188
|
-
# Do not use deprecated static attributes
|
189
|
-
admin true
|
190
|
-
|
191
|
-
# Use dynamic attributes instead
|
192
|
-
admin { true }
|
193
|
-
end
|
194
|
-
```
|
295
|
+
Note that objects created with `build_stubbed` cannot be serialized with
|
296
|
+
`Marshal.dump`, since factory\_bot defines singleton methods on these objects.
|
195
297
|
|
196
298
|
Aliases
|
197
299
|
-------
|
198
|
-
|
300
|
+
|
301
|
+
factory\_bot allows you to define aliases to existing factories to make them
|
302
|
+
easier to re-use. This could come in handy when, for example, your Post object
|
303
|
+
has an author attribute that actually refers to an instance of a User class.
|
304
|
+
While normally factory\_bot can infer the factory name from the association name,
|
305
|
+
in this case it will look for an author factory in vain. So, alias your user
|
306
|
+
factory so it can be used under alias names.
|
199
307
|
|
200
308
|
```ruby
|
201
309
|
factory :user, aliases: [:author, :commenter] do
|
@@ -205,17 +313,17 @@ factory :user, aliases: [:author, :commenter] do
|
|
205
313
|
end
|
206
314
|
|
207
315
|
factory :post do
|
208
|
-
author
|
209
|
-
# instead of
|
316
|
+
# The alias allows us to write author instead of
|
210
317
|
# association :author, factory: :user
|
318
|
+
author
|
211
319
|
title { "How to read a book effectively" }
|
212
320
|
body { "There are five steps involved." }
|
213
321
|
end
|
214
322
|
|
215
323
|
factory :comment do
|
216
|
-
commenter
|
217
|
-
# instead of
|
324
|
+
# The alias allows us to write commenter instead of
|
218
325
|
# association :commenter, factory: :user
|
326
|
+
commenter
|
219
327
|
body { "Great article!" }
|
220
328
|
end
|
221
329
|
```
|
@@ -239,41 +347,84 @@ create(:user, last_name: "Doe").email
|
|
239
347
|
|
240
348
|
Transient Attributes
|
241
349
|
--------------------
|
350
|
+
Transient attributes are attributes only available within the factory definition, and not set on the object being built. This allows for more complex logic inside factories.
|
242
351
|
|
243
|
-
|
352
|
+
### With other attributes
|
353
|
+
|
354
|
+
There may be times where your code can be DRYed up by passing in transient
|
355
|
+
attributes to factories. You can access transient attributes within other
|
356
|
+
attributes (see [Dependent Attributes](#dependent-attributes)):
|
244
357
|
|
245
358
|
```ruby
|
246
359
|
factory :user do
|
247
360
|
transient do
|
248
361
|
rockstar { true }
|
249
|
-
upcased { false }
|
250
362
|
end
|
251
363
|
|
252
364
|
name { "John Doe#{" - Rockstar" if rockstar}" }
|
253
|
-
|
365
|
+
end
|
366
|
+
|
367
|
+
create(:user).name
|
368
|
+
#=> "John Doe - ROCKSTAR"
|
369
|
+
|
370
|
+
create(:user, rockstar: false).name
|
371
|
+
#=> "John Doe"
|
372
|
+
```
|
373
|
+
|
374
|
+
### With `attributes_for`
|
375
|
+
|
376
|
+
Transient attributes will be ignored within attributes\_for and won't be set on
|
377
|
+
the model, even if the attribute exists or you attempt to override it.
|
378
|
+
|
379
|
+
### With callbacks
|
380
|
+
|
381
|
+
If you need to access the evaluator in a factory\_bot callback,
|
382
|
+
you'll need to declare a second block argument (for the evaluator) and access
|
383
|
+
transient attributes from there.
|
384
|
+
|
385
|
+
```ruby
|
386
|
+
factory :user do
|
387
|
+
transient do
|
388
|
+
upcased { false }
|
389
|
+
end
|
390
|
+
|
391
|
+
name { "John Doe" }
|
254
392
|
|
255
393
|
after(:create) do |user, evaluator|
|
256
394
|
user.name.upcase! if evaluator.upcased
|
257
395
|
end
|
258
396
|
end
|
259
397
|
|
398
|
+
create(:user).name
|
399
|
+
#=> "John Doe"
|
400
|
+
|
260
401
|
create(:user, upcased: true).name
|
261
|
-
#=> "JOHN DOE
|
402
|
+
#=> "JOHN DOE"
|
262
403
|
```
|
263
404
|
|
264
|
-
|
265
|
-
attributes will be ignored within attributes\_for and won't be set on the model,
|
266
|
-
even if the attribute exists or you attempt to override it.
|
405
|
+
### With associations
|
267
406
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
407
|
+
Transient [associations](#associations) are not supported in factory\_bot.
|
408
|
+
Associations within the transient block will be treated as regular,
|
409
|
+
non-transient associations.
|
410
|
+
|
411
|
+
If needed, you can generally work around this by building a factory within a
|
412
|
+
transient attribute:
|
413
|
+
|
414
|
+
```ruby
|
415
|
+
factory :post
|
416
|
+
|
417
|
+
factory :user do
|
418
|
+
transient do
|
419
|
+
post { build(:post) }
|
420
|
+
end
|
421
|
+
end
|
422
|
+
```
|
272
423
|
|
273
424
|
Method Name / Reserved Word Attributes
|
274
425
|
-------------------------------
|
275
426
|
|
276
|
-
If your attributes conflict with existing methods or reserved words you can define them with `add_attribute`.
|
427
|
+
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`.
|
277
428
|
|
278
429
|
```ruby
|
279
430
|
factory :dna do
|
@@ -289,7 +440,10 @@ end
|
|
289
440
|
Inheritance
|
290
441
|
-----------
|
291
442
|
|
292
|
-
|
443
|
+
### Nested factories
|
444
|
+
|
445
|
+
You can easily create multiple factories for the same class without repeating
|
446
|
+
common attributes by nesting factories:
|
293
447
|
|
294
448
|
```ruby
|
295
449
|
factory :post do
|
@@ -305,6 +459,8 @@ approved_post.title # => "A title"
|
|
305
459
|
approved_post.approved # => true
|
306
460
|
```
|
307
461
|
|
462
|
+
### Assigning parent explicitly
|
463
|
+
|
308
464
|
You can also assign the parent explicitly:
|
309
465
|
|
310
466
|
```ruby
|
@@ -317,6 +473,8 @@ factory :approved_post, parent: :post do
|
|
317
473
|
end
|
318
474
|
```
|
319
475
|
|
476
|
+
### Best practices
|
477
|
+
|
320
478
|
As mentioned above, it's good practice to define a basic factory for each class
|
321
479
|
with only the attributes required to create it. Then, create more specific
|
322
480
|
factories that inherit from this basic parent. Factory definitions are still
|
@@ -325,7 +483,10 @@ code, so keep them DRY.
|
|
325
483
|
Associations
|
326
484
|
------------
|
327
485
|
|
328
|
-
|
486
|
+
### Implicit definition
|
487
|
+
|
488
|
+
It's possible to set up associations within factories. If the factory name is
|
489
|
+
the same as the association name, the factory name can be left out.
|
329
490
|
|
330
491
|
```ruby
|
331
492
|
factory :post do
|
@@ -334,18 +495,146 @@ factory :post do
|
|
334
495
|
end
|
335
496
|
```
|
336
497
|
|
337
|
-
|
498
|
+
### Explicit definition
|
499
|
+
|
500
|
+
You can define associations explicitly. This can be handy especially when
|
501
|
+
[Overriding attributes](#overriding-attributes)
|
502
|
+
|
503
|
+
```ruby
|
504
|
+
factory :post do
|
505
|
+
# ...
|
506
|
+
association :author
|
507
|
+
end
|
508
|
+
```
|
509
|
+
|
510
|
+
### Inline definition
|
511
|
+
|
512
|
+
You can also define associations inline within regular attributes,
|
513
|
+
but note that the value will be `nil`
|
514
|
+
when using the `attributes_for` strategy.
|
515
|
+
|
516
|
+
```ruby
|
517
|
+
factory :post do
|
518
|
+
# ...
|
519
|
+
author { association :author }
|
520
|
+
end
|
521
|
+
```
|
522
|
+
|
523
|
+
### Specifying the factory
|
524
|
+
|
525
|
+
You can specify a different factory (although [Aliases](#aliases) might also
|
526
|
+
help you out here).
|
527
|
+
|
528
|
+
Implicitly:
|
338
529
|
|
339
530
|
```ruby
|
340
531
|
factory :post do
|
341
532
|
# ...
|
342
|
-
|
533
|
+
author factory: :user
|
343
534
|
end
|
344
535
|
```
|
345
536
|
|
346
|
-
|
537
|
+
Explicitly:
|
347
538
|
|
348
539
|
```ruby
|
540
|
+
factory :post do
|
541
|
+
# ...
|
542
|
+
association :author, factory: :user
|
543
|
+
end
|
544
|
+
```
|
545
|
+
|
546
|
+
Inline:
|
547
|
+
|
548
|
+
```ruby
|
549
|
+
factory :post do
|
550
|
+
# ...
|
551
|
+
author { association :user }
|
552
|
+
end
|
553
|
+
```
|
554
|
+
|
555
|
+
### Overriding attributes
|
556
|
+
|
557
|
+
You can also override attributes.
|
558
|
+
|
559
|
+
Implicitly:
|
560
|
+
|
561
|
+
```ruby
|
562
|
+
factory :post do
|
563
|
+
# ...
|
564
|
+
author factory: :author, last_name: "Writely"
|
565
|
+
end
|
566
|
+
```
|
567
|
+
|
568
|
+
Explicitly:
|
569
|
+
|
570
|
+
|
571
|
+
```ruby
|
572
|
+
factory :post do
|
573
|
+
# ...
|
574
|
+
association :author, last_name: "Writely"
|
575
|
+
end
|
576
|
+
```
|
577
|
+
|
578
|
+
Or inline using attributes from the factory:
|
579
|
+
|
580
|
+
```rb
|
581
|
+
factory :post do
|
582
|
+
# ...
|
583
|
+
author_last_name { "Writely" }
|
584
|
+
author { association :author, last_name: author_last_name }
|
585
|
+
end
|
586
|
+
```
|
587
|
+
|
588
|
+
### Association overrides
|
589
|
+
|
590
|
+
Attribute overrides can be used to link associated objects:
|
591
|
+
|
592
|
+
```ruby
|
593
|
+
FactoryBot.define do
|
594
|
+
factory :author do
|
595
|
+
name { 'Taylor' }
|
596
|
+
end
|
597
|
+
|
598
|
+
factory :post do
|
599
|
+
author
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
eunji = build(:author, name: 'Eunji')
|
604
|
+
post = build(:post, author: eunji)
|
605
|
+
```
|
606
|
+
|
607
|
+
### Build strategies
|
608
|
+
|
609
|
+
In factory\_bot 5, associations default to using the same build strategy as
|
610
|
+
their parent object:
|
611
|
+
|
612
|
+
```ruby
|
613
|
+
FactoryBot.define do
|
614
|
+
factory :author
|
615
|
+
|
616
|
+
factory :post do
|
617
|
+
author
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
post = build(:post)
|
622
|
+
post.new_record? # => true
|
623
|
+
post.author.new_record? # => true
|
624
|
+
|
625
|
+
post = create(:post)
|
626
|
+
post.new_record? # => false
|
627
|
+
post.author.new_record? # => false
|
628
|
+
```
|
629
|
+
|
630
|
+
This is different than the default behavior for previous versions of
|
631
|
+
factory\_bot, where the association strategy would not always match the strategy
|
632
|
+
of the parent object. If you want to continue using the old behavior, you can
|
633
|
+
set the `use_parent_strategy` configuration option to `false`.
|
634
|
+
|
635
|
+
```ruby
|
636
|
+
FactoryBot.use_parent_strategy = false
|
637
|
+
|
349
638
|
# Builds and saves a User and a Post
|
350
639
|
post = create(:post)
|
351
640
|
post.new_record? # => false
|
@@ -357,9 +646,11 @@ post.new_record? # => true
|
|
357
646
|
post.author.new_record? # => false
|
358
647
|
```
|
359
648
|
|
360
|
-
To not save the associated object, specify strategy: :build in the factory:
|
649
|
+
To not save the associated object, specify `strategy: :build` in the factory:
|
361
650
|
|
362
651
|
```ruby
|
652
|
+
FactoryBot.use_parent_strategy = false
|
653
|
+
|
363
654
|
factory :post do
|
364
655
|
# ...
|
365
656
|
association :author, factory: :user, strategy: :build
|
@@ -380,27 +671,53 @@ factory :post do
|
|
380
671
|
author strategy: :build # <<< this does *not* work; causes author_id to be nil
|
381
672
|
```
|
382
673
|
|
383
|
-
|
384
|
-
|
385
|
-
|
674
|
+
### `has_many` associations
|
675
|
+
|
676
|
+
There are a few ways to generate data for a `has_many` relationship. The
|
677
|
+
simplest approach is to write a helper method in plain Ruby to tie together the
|
678
|
+
different records:
|
386
679
|
|
387
680
|
```ruby
|
388
681
|
FactoryBot.define do
|
682
|
+
factory :post do
|
683
|
+
title { "Through the Looking Glass" }
|
684
|
+
user
|
685
|
+
end
|
686
|
+
|
687
|
+
factory :user do
|
688
|
+
name { "Rachel Sanchez" }
|
689
|
+
end
|
690
|
+
end
|
389
691
|
|
390
|
-
|
692
|
+
def user_with_posts(posts_count: 5)
|
693
|
+
FactoryBot.create(:user) do |user|
|
694
|
+
FactoryBot.create_list(:post, posts_count, user: user)
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
create(:user).posts.length # 0
|
699
|
+
user_with_posts.posts.length # 5
|
700
|
+
user_with_posts(posts_count: 15).posts.length # 15
|
701
|
+
```
|
702
|
+
|
703
|
+
If you prefer to keep the object creation fully within factory\_bot, you can
|
704
|
+
build the posts in an `after(:create)` callback.
|
705
|
+
|
706
|
+
|
707
|
+
```ruby
|
708
|
+
FactoryBot.define do
|
391
709
|
factory :post do
|
392
710
|
title { "Through the Looking Glass" }
|
393
711
|
user
|
394
712
|
end
|
395
713
|
|
396
|
-
# user factory without associated posts
|
397
714
|
factory :user do
|
398
715
|
name { "John Doe" }
|
399
716
|
|
400
717
|
# user_with_posts will create post data after the user has been created
|
401
718
|
factory :user_with_posts do
|
402
|
-
# posts_count is declared as a transient attribute
|
403
|
-
#
|
719
|
+
# posts_count is declared as a transient attribute available in the
|
720
|
+
# callback via the evaluator
|
404
721
|
transient do
|
405
722
|
posts_count { 5 }
|
406
723
|
end
|
@@ -411,58 +728,142 @@ FactoryBot.define do
|
|
411
728
|
# to create and we make sure the user is associated properly to the post
|
412
729
|
after(:create) do |user, evaluator|
|
413
730
|
create_list(:post, evaluator.posts_count, user: user)
|
731
|
+
|
732
|
+
# You may need to reload the record here, depending on your application
|
733
|
+
user.reload
|
414
734
|
end
|
415
735
|
end
|
416
736
|
end
|
417
737
|
end
|
738
|
+
|
739
|
+
create(:user).posts.length # 0
|
740
|
+
create(:user_with_posts).posts.length # 5
|
741
|
+
create(:user_with_posts, posts_count: 15).posts.length # 15
|
418
742
|
```
|
419
743
|
|
420
|
-
|
744
|
+
Or, for a solution that works with `build`, `build_stubbed`, and `create`
|
745
|
+
(although it doesn't work well with `attributes_for`), you can use inline
|
746
|
+
associations:
|
421
747
|
|
422
748
|
```ruby
|
749
|
+
FactoryBot.define do
|
750
|
+
factory :post do
|
751
|
+
title { "Through the Looking Glass" }
|
752
|
+
user
|
753
|
+
end
|
754
|
+
|
755
|
+
factory :user do
|
756
|
+
name { "Taylor Kim" }
|
757
|
+
|
758
|
+
factory :user_with_posts do
|
759
|
+
posts { [association(:post)] }
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
423
764
|
create(:user).posts.length # 0
|
765
|
+
create(:user_with_posts).posts.length # 1
|
766
|
+
build(:user_with_posts).posts.length # 1
|
767
|
+
build_stubbed(:user_with_posts).posts.length # 1
|
768
|
+
```
|
769
|
+
|
770
|
+
For more flexibility you can combine this with the `posts_count` transient
|
771
|
+
attribute from the callback example:
|
772
|
+
|
773
|
+
```ruby
|
774
|
+
FactoryBot.define do
|
775
|
+
factory :post do
|
776
|
+
title { "Through the Looking Glass" }
|
777
|
+
user
|
778
|
+
end
|
779
|
+
|
780
|
+
factory :user do
|
781
|
+
name { "Adiza Kumato" }
|
782
|
+
|
783
|
+
factory :user_with_posts do
|
784
|
+
transient do
|
785
|
+
posts_count { 5 }
|
786
|
+
end
|
787
|
+
|
788
|
+
posts do
|
789
|
+
Array.new(posts_count) { association(:post) }
|
790
|
+
end
|
791
|
+
end
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
424
795
|
create(:user_with_posts).posts.length # 5
|
425
796
|
create(:user_with_posts, posts_count: 15).posts.length # 15
|
797
|
+
build(:user_with_posts, posts_count: 15).posts.length # 15
|
798
|
+
build_stubbed(:user_with_posts, posts_count: 15).posts.length # 15
|
426
799
|
```
|
427
800
|
|
801
|
+
### `has_and_belongs_to_many` associations
|
802
|
+
|
428
803
|
Generating data for a `has_and_belongs_to_many` relationship is very similar
|
429
|
-
to the above `has_many` relationship, with a small change
|
804
|
+
to the above `has_many` relationship, with a small change: you need to pass an
|
430
805
|
array of objects to the model's pluralized attribute name rather than a single
|
431
806
|
object to the singular version of the attribute name.
|
432
807
|
|
433
|
-
Here's an example with two models that are related via
|
434
|
-
`has_and_belongs_to_many`:
|
435
808
|
|
436
809
|
```ruby
|
437
|
-
|
810
|
+
def profile_with_languages(languages_count: 2)
|
811
|
+
FactoryBot.create(:profile) do |profile|
|
812
|
+
FactoryBot.create_list(:language, languages_count, profiles: [profile])
|
813
|
+
end
|
814
|
+
end
|
815
|
+
```
|
438
816
|
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
817
|
+
Or with the callback approach:
|
818
|
+
|
819
|
+
```ruby
|
820
|
+
factory :profile_with_languages do
|
821
|
+
transient do
|
822
|
+
languages_count { 2 }
|
443
823
|
end
|
444
824
|
|
445
|
-
|
446
|
-
|
447
|
-
|
825
|
+
after(:create) do |profile, evaluator|
|
826
|
+
create_list(:language, evaluator.languages_count, profiles: [profile])
|
827
|
+
profile.reload
|
828
|
+
end
|
829
|
+
end
|
830
|
+
```
|
448
831
|
|
449
|
-
|
450
|
-
|
451
|
-
factory :profile_with_languages do
|
452
|
-
# languages_count is declared as an ignored attribute and available in
|
453
|
-
# attributes on the factory, as well as the callback via the evaluator
|
454
|
-
transient do
|
455
|
-
languages_count { 5 }
|
456
|
-
end
|
832
|
+
Or the inline association approach (note the use of the `instance` method here
|
833
|
+
to refer to the profile being built):
|
457
834
|
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
835
|
+
```ruby
|
836
|
+
factory :profile_with_languages do
|
837
|
+
transient do
|
838
|
+
languages_count { 2 }
|
839
|
+
end
|
840
|
+
|
841
|
+
languages do
|
842
|
+
Array.new(languages_count) do
|
843
|
+
association(:language, profiles: [instance])
|
844
|
+
end
|
845
|
+
end
|
846
|
+
end
|
847
|
+
```
|
848
|
+
|
849
|
+
### Polymorphic associations
|
850
|
+
|
851
|
+
Polymorphic associations can be handled with traits:
|
852
|
+
|
853
|
+
```ruby
|
854
|
+
FactoryBot.define do
|
855
|
+
factory :video
|
856
|
+
factory :photo
|
857
|
+
|
858
|
+
factory :comment do
|
859
|
+
for_photo # default to the :for_photo trait if none is specified
|
860
|
+
|
861
|
+
trait :for_video do
|
862
|
+
association :commentable, factory: :video
|
863
|
+
end
|
864
|
+
|
865
|
+
trait :for_photo do
|
866
|
+
association :commentable, factory: :photo
|
466
867
|
end
|
467
868
|
end
|
468
869
|
end
|
@@ -471,14 +872,72 @@ end
|
|
471
872
|
This allows us to do:
|
472
873
|
|
473
874
|
```ruby
|
474
|
-
create(:
|
475
|
-
create(:
|
476
|
-
create(:
|
875
|
+
create(:comment)
|
876
|
+
create(:comment, :for_video)
|
877
|
+
create(:comment, :for_photo)
|
477
878
|
```
|
478
879
|
|
880
|
+
### Interconnected associations
|
881
|
+
|
882
|
+
There are limitless ways objects might be interconnected, and
|
883
|
+
factory\_bot may not always be suited to handle those relationships. In some
|
884
|
+
cases it makes sense to use factory\_bot to build each individual object, and
|
885
|
+
then to write helper methods in plain Ruby to tie those objects together.
|
886
|
+
|
887
|
+
That said, some more complex, interconnected relationships can be built in factory\_bot
|
888
|
+
using inline associations with reference to the `instance` being built.
|
889
|
+
|
890
|
+
Let's say your models look like this, where an associated `Student` and
|
891
|
+
`Profile` should both belong to the same `School`:
|
892
|
+
|
893
|
+
```ruby
|
894
|
+
class Student < ApplicationRecord
|
895
|
+
belongs_to :school
|
896
|
+
has_one :profile
|
897
|
+
end
|
898
|
+
|
899
|
+
class Profile < ApplicationRecord
|
900
|
+
belongs_to :school
|
901
|
+
belongs_to :student
|
902
|
+
end
|
903
|
+
|
904
|
+
class School < ApplicationRecord
|
905
|
+
has_many :students
|
906
|
+
has_many :profiles
|
907
|
+
end
|
908
|
+
```
|
909
|
+
|
910
|
+
We can ensure the student and profile are connected to each other and to the
|
911
|
+
same school with a factory like this:
|
912
|
+
|
913
|
+
```ruby
|
914
|
+
FactoryBot.define do
|
915
|
+
factory :student do
|
916
|
+
school
|
917
|
+
profile { association :profile, student: instance, school: school }
|
918
|
+
end
|
919
|
+
|
920
|
+
factory :profile do
|
921
|
+
school
|
922
|
+
student { association :student, profile: instance, school: school }
|
923
|
+
end
|
924
|
+
|
925
|
+
factory :school
|
926
|
+
end
|
927
|
+
```
|
928
|
+
|
929
|
+
Note that this approach works with `build`, `build_stubbed`, and `create`, but
|
930
|
+
the associations will return `nil` when using `attributes_for`.
|
931
|
+
|
932
|
+
Also, note that if you assign any attributes inside a custom `initialize_with`
|
933
|
+
(e.g. `initialize_with { new(**attributes) }`), those attributes should not refer to `instance`,
|
934
|
+
since it will be `nil`.
|
935
|
+
|
479
936
|
Sequences
|
480
937
|
---------
|
481
938
|
|
939
|
+
### Global sequences
|
940
|
+
|
482
941
|
Unique values in a specific format (for example, e-mail addresses) can be
|
483
942
|
generated using sequences. Sequences are defined by calling `sequence` in a
|
484
943
|
definition block, and values in a sequence are generated by calling
|
@@ -499,6 +958,8 @@ generate :email
|
|
499
958
|
# => "person2@example.com"
|
500
959
|
```
|
501
960
|
|
961
|
+
### With dynamic attributes
|
962
|
+
|
502
963
|
Sequences can be used in dynamic attributes:
|
503
964
|
|
504
965
|
```ruby
|
@@ -507,6 +968,8 @@ factory :invite do
|
|
507
968
|
end
|
508
969
|
```
|
509
970
|
|
971
|
+
### As implicit attributes
|
972
|
+
|
510
973
|
Or as implicit attributes:
|
511
974
|
|
512
975
|
```ruby
|
@@ -515,6 +978,11 @@ factory :user do
|
|
515
978
|
end
|
516
979
|
```
|
517
980
|
|
981
|
+
Note that defining sequences as implicit attributes will not work if you have a
|
982
|
+
factory with the same name as the sequence.
|
983
|
+
|
984
|
+
### Inline sequences
|
985
|
+
|
518
986
|
And it's also possible to define an in-line sequence that is only used in
|
519
987
|
a particular factory:
|
520
988
|
|
@@ -524,7 +992,10 @@ factory :user do
|
|
524
992
|
end
|
525
993
|
```
|
526
994
|
|
527
|
-
|
995
|
+
### Initial value
|
996
|
+
|
997
|
+
You can override the initial value. Any value that responds to the `#next`
|
998
|
+
method will work (e.g. 1, 2, 3, 'a', 'b', 'c')
|
528
999
|
|
529
1000
|
```ruby
|
530
1001
|
factory :user do
|
@@ -532,6 +1003,8 @@ factory :user do
|
|
532
1003
|
end
|
533
1004
|
```
|
534
1005
|
|
1006
|
+
### Without a block
|
1007
|
+
|
535
1008
|
Without a block, the value will increment itself, starting at its initial value:
|
536
1009
|
|
537
1010
|
```ruby
|
@@ -540,6 +1013,17 @@ factory :post do
|
|
540
1013
|
end
|
541
1014
|
```
|
542
1015
|
|
1016
|
+
Please note, that the value for the sequence could be any Enumerable instance,
|
1017
|
+
as long as it responds to `#next`:
|
1018
|
+
|
1019
|
+
```ruby
|
1020
|
+
factory :task do
|
1021
|
+
sequence :priority, %i[low medium high urgent].cycle
|
1022
|
+
end
|
1023
|
+
```
|
1024
|
+
|
1025
|
+
### Aliases
|
1026
|
+
|
543
1027
|
Sequences can also have aliases. The sequence aliases share the same counter:
|
544
1028
|
|
545
1029
|
```ruby
|
@@ -569,6 +1053,8 @@ end
|
|
569
1053
|
|
570
1054
|
The value just needs to support the `#next` method. Here the next value will be 'a', then 'b', etc.
|
571
1055
|
|
1056
|
+
### Rewinding
|
1057
|
+
|
572
1058
|
Sequences can also be rewound with `FactoryBot.rewind_sequences`:
|
573
1059
|
|
574
1060
|
```ruby
|
@@ -585,9 +1071,27 @@ generate(:email) # "person1@example.com"
|
|
585
1071
|
|
586
1072
|
This rewinds all registered sequences.
|
587
1073
|
|
1074
|
+
### Uniqueness
|
1075
|
+
|
1076
|
+
When working with uniqueness constraints, be careful not to pass in override values that will conflict with the generated sequence values.
|
1077
|
+
|
1078
|
+
In this example the email will be the same for both users. If email must be unique, this code will error:
|
1079
|
+
|
1080
|
+
```rb
|
1081
|
+
factory :user do
|
1082
|
+
sequence(:email) { |n| "person#{n}@example.com" }
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
FactoryBot.create(:user, email: "person1@example.com")
|
1086
|
+
FactoryBot.create(:user)
|
1087
|
+
```
|
1088
|
+
|
1089
|
+
|
588
1090
|
Traits
|
589
1091
|
------
|
590
1092
|
|
1093
|
+
### Defining traits
|
1094
|
+
|
591
1095
|
Traits allow you to group attributes together and then apply them
|
592
1096
|
to any factory.
|
593
1097
|
|
@@ -623,7 +1127,9 @@ factory :story do
|
|
623
1127
|
end
|
624
1128
|
```
|
625
1129
|
|
626
|
-
|
1130
|
+
### As implicit attributes
|
1131
|
+
|
1132
|
+
Traits can be used as implicit attributes:
|
627
1133
|
|
628
1134
|
```ruby
|
629
1135
|
factory :week_long_published_story_with_title, parent: :story do
|
@@ -633,6 +1139,11 @@ factory :week_long_published_story_with_title, parent: :story do
|
|
633
1139
|
end
|
634
1140
|
```
|
635
1141
|
|
1142
|
+
Note that defining traits as implicit attributes will not work if you have a
|
1143
|
+
factory or sequence with the same name as the trait.
|
1144
|
+
|
1145
|
+
### Attribute precedence
|
1146
|
+
|
636
1147
|
Traits that define the same attributes won't raise AttributeDefinitionErrors;
|
637
1148
|
the trait that defines the attribute latest gets precedence.
|
638
1149
|
|
@@ -641,16 +1152,16 @@ factory :user do
|
|
641
1152
|
name { "Friendly User" }
|
642
1153
|
login { name }
|
643
1154
|
|
644
|
-
trait :
|
1155
|
+
trait :active do
|
645
1156
|
name { "John Doe" }
|
646
|
-
|
647
|
-
login { "#{name} (
|
1157
|
+
status { :active }
|
1158
|
+
login { "#{name} (active)" }
|
648
1159
|
end
|
649
1160
|
|
650
|
-
trait :
|
1161
|
+
trait :inactive do
|
651
1162
|
name { "Jane Doe" }
|
652
|
-
|
653
|
-
login { "#{name} (
|
1163
|
+
status { :inactive }
|
1164
|
+
login { "#{name} (inactive)" }
|
654
1165
|
end
|
655
1166
|
|
656
1167
|
trait :admin do
|
@@ -658,40 +1169,67 @@ factory :user do
|
|
658
1169
|
login { "admin-#{name}" }
|
659
1170
|
end
|
660
1171
|
|
661
|
-
factory :
|
662
|
-
factory :
|
1172
|
+
factory :active_admin, traits: [:active, :admin] # login will be "admin-John Doe"
|
1173
|
+
factory :inactive_admin, traits: [:admin, :inactive] # login will be "Jane Doe (inactive)"
|
663
1174
|
end
|
664
1175
|
```
|
665
1176
|
|
666
|
-
|
1177
|
+
### In child factories
|
1178
|
+
|
1179
|
+
You can override individual attributes granted by a trait in a child factory:
|
667
1180
|
|
668
1181
|
```ruby
|
669
1182
|
factory :user do
|
670
1183
|
name { "Friendly User" }
|
671
1184
|
login { name }
|
672
1185
|
|
673
|
-
trait :
|
1186
|
+
trait :active do
|
674
1187
|
name { "John Doe" }
|
675
|
-
|
1188
|
+
status { :active }
|
676
1189
|
login { "#{name} (M)" }
|
677
1190
|
end
|
678
1191
|
|
679
1192
|
factory :brandon do
|
680
|
-
|
1193
|
+
active
|
681
1194
|
name { "Brandon" }
|
682
1195
|
end
|
683
1196
|
end
|
684
1197
|
```
|
685
1198
|
|
686
|
-
|
1199
|
+
### As mixins
|
1200
|
+
|
1201
|
+
Traits can be defined outside of factories and used as mixins to compose shared attributes
|
1202
|
+
|
1203
|
+
```ruby
|
1204
|
+
FactoryBot.define do
|
1205
|
+
trait :timestamps do
|
1206
|
+
created_at { 8.days.ago }
|
1207
|
+
updated_at { 4.days.ago }
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
factory :user, traits: [:timestamps] do
|
1211
|
+
username { "john_doe" }
|
1212
|
+
end
|
1213
|
+
|
1214
|
+
factory :post do
|
1215
|
+
timestamps
|
1216
|
+
title { "Traits rock" }
|
1217
|
+
end
|
1218
|
+
end
|
1219
|
+
```
|
1220
|
+
|
1221
|
+
### Using traits
|
1222
|
+
|
1223
|
+
Traits can also be passed in as a list of symbols when you construct an instance
|
1224
|
+
from factory\_bot.
|
687
1225
|
|
688
1226
|
```ruby
|
689
1227
|
factory :user do
|
690
1228
|
name { "Friendly User" }
|
691
1229
|
|
692
|
-
trait :
|
1230
|
+
trait :active do
|
693
1231
|
name { "John Doe" }
|
694
|
-
|
1232
|
+
status { :active }
|
695
1233
|
end
|
696
1234
|
|
697
1235
|
trait :admin do
|
@@ -699,8 +1237,8 @@ factory :user do
|
|
699
1237
|
end
|
700
1238
|
end
|
701
1239
|
|
702
|
-
# creates an admin user with
|
703
|
-
create(:user, :admin, :
|
1240
|
+
# creates an admin user with :active status and name "Jon Snow"
|
1241
|
+
create(:user, :admin, :active, name: "Jon Snow")
|
704
1242
|
```
|
705
1243
|
|
706
1244
|
This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`.
|
@@ -718,10 +1256,12 @@ factory :user do
|
|
718
1256
|
end
|
719
1257
|
end
|
720
1258
|
|
721
|
-
# creates 3 admin users with
|
722
|
-
create_list(:user, 3, :admin, :
|
1259
|
+
# creates 3 admin users with :active status and name "Jon Snow"
|
1260
|
+
create_list(:user, 3, :admin, :active, name: "Jon Snow")
|
723
1261
|
```
|
724
1262
|
|
1263
|
+
### With associations
|
1264
|
+
|
725
1265
|
Traits can be used with associations easily too:
|
726
1266
|
|
727
1267
|
```ruby
|
@@ -762,6 +1302,8 @@ end
|
|
762
1302
|
create(:post).author
|
763
1303
|
```
|
764
1304
|
|
1305
|
+
### Traits within traits
|
1306
|
+
|
765
1307
|
Traits can be used within other traits to mix in their attributes.
|
766
1308
|
|
767
1309
|
```ruby
|
@@ -777,6 +1319,8 @@ factory :order do
|
|
777
1319
|
end
|
778
1320
|
```
|
779
1321
|
|
1322
|
+
### With transient attributes
|
1323
|
+
|
780
1324
|
Finally, traits can accept transient attributes.
|
781
1325
|
|
782
1326
|
```ruby
|
@@ -795,9 +1339,103 @@ end
|
|
795
1339
|
create :invoice, :with_amount, amount: 2
|
796
1340
|
```
|
797
1341
|
|
1342
|
+
### Enum traits
|
1343
|
+
|
1344
|
+
Given an Active Record model with an enum attribute:
|
1345
|
+
|
1346
|
+
```rb
|
1347
|
+
class Task < ActiveRecord::Base
|
1348
|
+
enum status: {queued: 0, started: 1, finished: 2}
|
1349
|
+
end
|
1350
|
+
|
1351
|
+
```
|
1352
|
+
|
1353
|
+
factory\_bot will automatically define traits for each possible value of the
|
1354
|
+
enum:
|
1355
|
+
|
1356
|
+
```rb
|
1357
|
+
FactoryBot.define do
|
1358
|
+
factory :task
|
1359
|
+
end
|
1360
|
+
|
1361
|
+
FactoryBot.build(:task, :queued)
|
1362
|
+
FactoryBot.build(:task, :started)
|
1363
|
+
FactoryBot.build(:task, :finished)
|
1364
|
+
```
|
1365
|
+
|
1366
|
+
Writing the traits out manually would be cumbersome, and is not necessary:
|
1367
|
+
|
1368
|
+
```rb
|
1369
|
+
FactoryBot.define do
|
1370
|
+
factory :task do
|
1371
|
+
trait :queued do
|
1372
|
+
status { :queued }
|
1373
|
+
end
|
1374
|
+
|
1375
|
+
trait :started do
|
1376
|
+
status { :started }
|
1377
|
+
end
|
1378
|
+
|
1379
|
+
trait :finished do
|
1380
|
+
status { :finished }
|
1381
|
+
end
|
1382
|
+
end
|
1383
|
+
end
|
1384
|
+
```
|
1385
|
+
|
1386
|
+
If automatically defining traits for enum attributes on every factory is not
|
1387
|
+
desired, it is possible to disable the feature by setting
|
1388
|
+
`FactoryBot.automatically_define_enum_traits = false`
|
1389
|
+
|
1390
|
+
In that case, it is still possible to explicitly define traits for an enum
|
1391
|
+
attribute in a particular factory:
|
1392
|
+
|
1393
|
+
```rb
|
1394
|
+
FactoryBot.automatically_define_enum_traits = false
|
1395
|
+
|
1396
|
+
FactoryBot.define do
|
1397
|
+
factory :task do
|
1398
|
+
traits_for_enum(:status)
|
1399
|
+
end
|
1400
|
+
end
|
1401
|
+
```
|
1402
|
+
|
1403
|
+
It is also possible to use this feature for other enumerable values, not
|
1404
|
+
specifically tied to Active Record enum attributes.
|
1405
|
+
|
1406
|
+
With an array:
|
1407
|
+
|
1408
|
+
```rb
|
1409
|
+
class Task
|
1410
|
+
attr_accessor :status
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
FactoryBot.define do
|
1414
|
+
factory :task do
|
1415
|
+
traits_for_enum(:status, ["queued", "started", "finished"])
|
1416
|
+
end
|
1417
|
+
end
|
1418
|
+
```
|
1419
|
+
|
1420
|
+
Or with a hash:
|
1421
|
+
|
1422
|
+
```rb
|
1423
|
+
class Task
|
1424
|
+
attr_accessor :status
|
1425
|
+
end
|
1426
|
+
|
1427
|
+
FactoryBot.define do
|
1428
|
+
factory :task do
|
1429
|
+
traits_for_enum(:status, { queued: 0, started: 1, finished: 2 })
|
1430
|
+
end
|
1431
|
+
end
|
1432
|
+
```
|
1433
|
+
|
798
1434
|
Callbacks
|
799
1435
|
---------
|
800
1436
|
|
1437
|
+
### Default callbacks
|
1438
|
+
|
801
1439
|
factory\_bot makes available four callbacks for injecting some code:
|
802
1440
|
|
803
1441
|
* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`)
|
@@ -816,6 +1454,8 @@ end
|
|
816
1454
|
|
817
1455
|
Note that you'll have an instance of the user in the block. This can be useful.
|
818
1456
|
|
1457
|
+
### Multiple callbacks
|
1458
|
+
|
819
1459
|
You can also define multiple types of callbacks on the same factory:
|
820
1460
|
|
821
1461
|
```ruby
|
@@ -825,7 +1465,8 @@ factory :user do
|
|
825
1465
|
end
|
826
1466
|
```
|
827
1467
|
|
828
|
-
Factories can also define any number of the same kind of callback. These
|
1468
|
+
Factories can also define any number of the same kind of callback. These
|
1469
|
+
callbacks will be executed in the order they are specified:
|
829
1470
|
|
830
1471
|
```ruby
|
831
1472
|
factory :user do
|
@@ -836,9 +1477,12 @@ end
|
|
836
1477
|
|
837
1478
|
Calling `create` will invoke both `after_build` and `after_create` callbacks.
|
838
1479
|
|
839
|
-
Also, like standard attributes, child factories will inherit (and can also
|
1480
|
+
Also, like standard attributes, child factories will inherit (and can also
|
1481
|
+
define) callbacks from their parent factory.
|
840
1482
|
|
841
|
-
Multiple callbacks can be assigned to run a block; this is useful when building
|
1483
|
+
Multiple callbacks can be assigned to run a block; this is useful when building
|
1484
|
+
various strategies that run the same code (since there are no callbacks that are
|
1485
|
+
shared across all strategies).
|
842
1486
|
|
843
1487
|
```ruby
|
844
1488
|
factory :user do
|
@@ -848,6 +1492,8 @@ factory :user do
|
|
848
1492
|
end
|
849
1493
|
```
|
850
1494
|
|
1495
|
+
### Global callbacks
|
1496
|
+
|
851
1497
|
To override callbacks for all factories, define them within the
|
852
1498
|
`FactoryBot.define` block:
|
853
1499
|
|
@@ -862,7 +1508,9 @@ FactoryBot.define do
|
|
862
1508
|
end
|
863
1509
|
```
|
864
1510
|
|
865
|
-
|
1511
|
+
### Symbol#to_proc
|
1512
|
+
|
1513
|
+
You can call callbacks that rely on `Symbol#to_proc`:
|
866
1514
|
|
867
1515
|
```ruby
|
868
1516
|
# app/models/user.rb
|
@@ -885,15 +1533,16 @@ create(:user) # creates the user and confirms it
|
|
885
1533
|
Modifying factories
|
886
1534
|
-------------------
|
887
1535
|
|
888
|
-
If you're given a set of factories (say, from a gem developer) but want to
|
889
|
-
|
1536
|
+
If you're given a set of factories (say, from a gem developer) but want to
|
1537
|
+
change them to fit into your application better, you can modify that factory
|
1538
|
+
instead of creating a child factory and adding attributes there.
|
890
1539
|
|
891
1540
|
If a gem were to give you a User factory:
|
892
1541
|
|
893
1542
|
```ruby
|
894
1543
|
FactoryBot.define do
|
895
1544
|
factory :user do
|
896
|
-
full_name "John Doe"
|
1545
|
+
full_name { "John Doe" }
|
897
1546
|
sequence(:username) { |n| "user#{n}" }
|
898
1547
|
password { "password" }
|
899
1548
|
end
|
@@ -907,7 +1556,6 @@ FactoryBot.define do
|
|
907
1556
|
factory :application_user, parent: :user do
|
908
1557
|
full_name { "Jane Doe" }
|
909
1558
|
date_of_birth { 21.years.ago }
|
910
|
-
gender { "Female" }
|
911
1559
|
health { 90 }
|
912
1560
|
end
|
913
1561
|
end
|
@@ -920,7 +1568,6 @@ FactoryBot.modify do
|
|
920
1568
|
factory :user do
|
921
1569
|
full_name { "Jane Doe" }
|
922
1570
|
date_of_birth { 21.years.ago }
|
923
|
-
gender { "Female" }
|
924
1571
|
health { 90 }
|
925
1572
|
end
|
926
1573
|
end
|
@@ -950,6 +1597,23 @@ To set the attributes for each of the factories, you can pass in a hash as you n
|
|
950
1597
|
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
|
951
1598
|
```
|
952
1599
|
|
1600
|
+
In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters:
|
1601
|
+
|
1602
|
+
```ruby
|
1603
|
+
twenty_somethings = build_list(:user, 10) do |user, i|
|
1604
|
+
user.date_of_birth = (20 + i).years.ago
|
1605
|
+
end
|
1606
|
+
```
|
1607
|
+
|
1608
|
+
`create_list` passes saved instances into the block. If you modify the instance, you must save it again:
|
1609
|
+
|
1610
|
+
```ruby
|
1611
|
+
twenty_somethings = create_list(:user, 10) do |user, i|
|
1612
|
+
user.date_of_birth = (20 + i).years.ago
|
1613
|
+
user.save!
|
1614
|
+
end
|
1615
|
+
```
|
1616
|
+
|
953
1617
|
`build_stubbed_list` will give you fully stubbed out instances:
|
954
1618
|
|
955
1619
|
```ruby
|
@@ -972,7 +1636,7 @@ users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
|
|
972
1636
|
Linting Factories
|
973
1637
|
-----------------
|
974
1638
|
|
975
|
-
|
1639
|
+
factory\_bot allows for linting known factories:
|
976
1640
|
|
977
1641
|
```ruby
|
978
1642
|
FactoryBot.lint
|
@@ -999,8 +1663,10 @@ namespace :factory_bot do
|
|
999
1663
|
desc "Verify that all FactoryBot factories are valid"
|
1000
1664
|
task lint: :environment do
|
1001
1665
|
if Rails.env.test?
|
1002
|
-
|
1666
|
+
conn = ActiveRecord::Base.connection
|
1667
|
+
conn.transaction do
|
1003
1668
|
FactoryBot.lint
|
1669
|
+
raise ActiveRecord::Rollback
|
1004
1670
|
end
|
1005
1671
|
else
|
1006
1672
|
system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
|
@@ -1012,8 +1678,7 @@ end
|
|
1012
1678
|
|
1013
1679
|
After calling `FactoryBot.lint`, you'll likely want to clear out the
|
1014
1680
|
database, as records will most likely be created. The provided example above
|
1015
|
-
uses
|
1016
|
-
gem to your Gemfile under the appropriate groups.
|
1681
|
+
uses an sql transaction and rollback to leave the database clean.
|
1017
1682
|
|
1018
1683
|
You can lint factories selectively by passing only factories you want linted:
|
1019
1684
|
|
@@ -1047,10 +1712,17 @@ You can also specify the strategy used for linting:
|
|
1047
1712
|
FactoryBot.lint strategy: :build
|
1048
1713
|
```
|
1049
1714
|
|
1715
|
+
Verbose linting will include full backtraces for each error, which can be
|
1716
|
+
helpful for debugging:
|
1717
|
+
|
1718
|
+
```ruby
|
1719
|
+
FactoryBot.lint verbose: true
|
1720
|
+
```
|
1721
|
+
|
1050
1722
|
Custom Construction
|
1051
1723
|
-------------------
|
1052
1724
|
|
1053
|
-
If you want to use
|
1725
|
+
If you want to use factory\_bot to construct an object where some attributes
|
1054
1726
|
are passed to `initialize` or if you want to do something other than simply
|
1055
1727
|
calling `new` on your build class, you can override the default behavior by
|
1056
1728
|
defining `initialize_with` on your factory. Example:
|
@@ -1078,7 +1750,7 @@ end
|
|
1078
1750
|
build(:user).name # Jane Doe
|
1079
1751
|
```
|
1080
1752
|
|
1081
|
-
Although
|
1753
|
+
Although factory\_bot is written to work with ActiveRecord out of the box, it
|
1082
1754
|
can also work with any Ruby class. For maximum compatibility with ActiveRecord,
|
1083
1755
|
the default initializer builds all instances by calling `new` on your build class
|
1084
1756
|
without any arguments. It then calls attribute writer methods to assign all the
|
@@ -1089,7 +1761,7 @@ You can override the initializer in order to:
|
|
1089
1761
|
|
1090
1762
|
* Build non-ActiveRecord objects that require arguments to `initialize`
|
1091
1763
|
* Use a method other than `new` to instantiate the instance
|
1092
|
-
* Do
|
1764
|
+
* Do wild things like decorate the instance after it's built
|
1093
1765
|
|
1094
1766
|
When using `initialize_with`, you don't have to declare the class itself when
|
1095
1767
|
calling `new`; however, any other class methods you want to call will have to
|
@@ -1116,7 +1788,7 @@ factory :user do
|
|
1116
1788
|
|
1117
1789
|
name "John Doe"
|
1118
1790
|
|
1119
|
-
initialize_with { new(attributes) }
|
1791
|
+
initialize_with { new(**attributes) }
|
1120
1792
|
end
|
1121
1793
|
```
|
1122
1794
|
|
@@ -1151,7 +1823,7 @@ build(:user)
|
|
1151
1823
|
User.new('value')
|
1152
1824
|
```
|
1153
1825
|
|
1154
|
-
This prevents duplicate assignment; in versions of
|
1826
|
+
This prevents duplicate assignment; in versions of factory\_bot before 4.0, it
|
1155
1827
|
would run this:
|
1156
1828
|
|
1157
1829
|
```ruby
|
@@ -1337,12 +2009,12 @@ with associations, as below:
|
|
1337
2009
|
|
1338
2010
|
```ruby
|
1339
2011
|
FactoryBot.define do
|
1340
|
-
factory :united_states, class: Location do
|
2012
|
+
factory :united_states, class: "Location" do
|
1341
2013
|
name { 'United States' }
|
1342
2014
|
association :location_group, factory: :north_america
|
1343
2015
|
end
|
1344
2016
|
|
1345
|
-
factory :north_america, class: LocationGroup do
|
2017
|
+
factory :north_america, class: "LocationGroup" do
|
1346
2018
|
name { 'North America' }
|
1347
2019
|
end
|
1348
2020
|
end
|