factory_bot 4.11.1 → 6.2.0
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 +785 -153
- data/LICENSE +1 -1
- data/NEWS.md +379 -0
- data/README.md +20 -30
- data/lib/factory_bot/aliases.rb +2 -2
- 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 +24 -10
- 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/stub.rb +37 -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"
|
17
|
-
```
|
18
|
-
|
19
|
-
JRuby users: factory_bot works with JRuby starting with 1.6.7.2 (latest stable, as per July 2012).
|
20
|
-
JRuby has to be used in 1.9 mode, for that, use JRUBY_OPTS environment variable:
|
21
|
-
|
22
|
-
```bash
|
23
|
-
export JRUBY_OPTS=--1.9
|
99
|
+
gem "factory_bot"
|
24
100
|
```
|
25
101
|
|
26
|
-
|
102
|
+
### Configure your test suite
|
27
103
|
|
28
|
-
|
29
|
-
-------------------------
|
104
|
+
#### RSpec
|
30
105
|
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
-
------------------
|
182
|
-
|
183
|
-
Static attributes, without a block, are deprecated and will be removed in
|
184
|
-
factory\_bot 5.
|
185
|
-
|
186
|
-
```ruby
|
187
|
-
factory :user do
|
188
|
-
# Do not use deprecated static attributes
|
189
|
-
admin true
|
293
|
+
### `build_stubbed` and `Marshal.dump`
|
190
294
|
|
191
|
-
|
192
|
-
|
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.
|
351
|
+
|
352
|
+
### With other attributes
|
242
353
|
|
243
|
-
There may be times where your code can be DRYed up by passing in transient
|
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)
|
338
502
|
|
339
503
|
```ruby
|
340
504
|
factory :post do
|
341
505
|
# ...
|
342
|
-
association :author
|
506
|
+
association :author
|
343
507
|
end
|
344
508
|
```
|
345
509
|
|
346
|
-
|
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.
|
347
515
|
|
348
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:
|
529
|
+
|
530
|
+
```ruby
|
531
|
+
factory :post do
|
532
|
+
# ...
|
533
|
+
author factory: :user
|
534
|
+
end
|
535
|
+
```
|
536
|
+
|
537
|
+
Explicitly:
|
538
|
+
|
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
|
+
author_last_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
|
691
|
+
|
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
|
389
697
|
|
390
|
-
|
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)
|
878
|
+
```
|
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
|
477
927
|
```
|
478
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,8 @@ factory :post do
|
|
540
1013
|
end
|
541
1014
|
```
|
542
1015
|
|
1016
|
+
### Aliases
|
1017
|
+
|
543
1018
|
Sequences can also have aliases. The sequence aliases share the same counter:
|
544
1019
|
|
545
1020
|
```ruby
|
@@ -569,6 +1044,8 @@ end
|
|
569
1044
|
|
570
1045
|
The value just needs to support the `#next` method. Here the next value will be 'a', then 'b', etc.
|
571
1046
|
|
1047
|
+
### Rewinding
|
1048
|
+
|
572
1049
|
Sequences can also be rewound with `FactoryBot.rewind_sequences`:
|
573
1050
|
|
574
1051
|
```ruby
|
@@ -585,9 +1062,27 @@ generate(:email) # "person1@example.com"
|
|
585
1062
|
|
586
1063
|
This rewinds all registered sequences.
|
587
1064
|
|
1065
|
+
### Uniqueness
|
1066
|
+
|
1067
|
+
When working with uniqueness constraints, be careful not to pass in override values that will conflict with the generated sequence values.
|
1068
|
+
|
1069
|
+
In this example the email will be the same for both users. If email must be unique, this code will error:
|
1070
|
+
|
1071
|
+
```rb
|
1072
|
+
factory :user do
|
1073
|
+
sequence(:email) { |n| "person#{n}@example.com" }
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
FactoryBot.create(:user, email: "person1@example.com")
|
1077
|
+
FactoryBot.create(:user)
|
1078
|
+
```
|
1079
|
+
|
1080
|
+
|
588
1081
|
Traits
|
589
1082
|
------
|
590
1083
|
|
1084
|
+
### Defining traits
|
1085
|
+
|
591
1086
|
Traits allow you to group attributes together and then apply them
|
592
1087
|
to any factory.
|
593
1088
|
|
@@ -623,7 +1118,9 @@ factory :story do
|
|
623
1118
|
end
|
624
1119
|
```
|
625
1120
|
|
626
|
-
|
1121
|
+
### As implicit attributes
|
1122
|
+
|
1123
|
+
Traits can be used as implicit attributes:
|
627
1124
|
|
628
1125
|
```ruby
|
629
1126
|
factory :week_long_published_story_with_title, parent: :story do
|
@@ -633,6 +1130,11 @@ factory :week_long_published_story_with_title, parent: :story do
|
|
633
1130
|
end
|
634
1131
|
```
|
635
1132
|
|
1133
|
+
Note that defining traits as implicit attributes will not work if you have a
|
1134
|
+
factory or sequence with the same name as the trait.
|
1135
|
+
|
1136
|
+
### Attribute precedence
|
1137
|
+
|
636
1138
|
Traits that define the same attributes won't raise AttributeDefinitionErrors;
|
637
1139
|
the trait that defines the attribute latest gets precedence.
|
638
1140
|
|
@@ -641,16 +1143,16 @@ factory :user do
|
|
641
1143
|
name { "Friendly User" }
|
642
1144
|
login { name }
|
643
1145
|
|
644
|
-
trait :
|
1146
|
+
trait :active do
|
645
1147
|
name { "John Doe" }
|
646
|
-
|
647
|
-
login { "#{name} (
|
1148
|
+
status { :active }
|
1149
|
+
login { "#{name} (active)" }
|
648
1150
|
end
|
649
1151
|
|
650
|
-
trait :
|
1152
|
+
trait :inactive do
|
651
1153
|
name { "Jane Doe" }
|
652
|
-
|
653
|
-
login { "#{name} (
|
1154
|
+
status { :inactive }
|
1155
|
+
login { "#{name} (inactive)" }
|
654
1156
|
end
|
655
1157
|
|
656
1158
|
trait :admin do
|
@@ -658,40 +1160,45 @@ factory :user do
|
|
658
1160
|
login { "admin-#{name}" }
|
659
1161
|
end
|
660
1162
|
|
661
|
-
factory :
|
662
|
-
factory :
|
1163
|
+
factory :active_admin, traits: [:active, :admin] # login will be "admin-John Doe"
|
1164
|
+
factory :inactive_admin, traits: [:admin, :inactive] # login will be "Jane Doe (inactive)"
|
663
1165
|
end
|
664
1166
|
```
|
665
1167
|
|
666
|
-
|
1168
|
+
### In child factories
|
1169
|
+
|
1170
|
+
You can override individual attributes granted by a trait in a child factory:
|
667
1171
|
|
668
1172
|
```ruby
|
669
1173
|
factory :user do
|
670
1174
|
name { "Friendly User" }
|
671
1175
|
login { name }
|
672
1176
|
|
673
|
-
trait :
|
1177
|
+
trait :active do
|
674
1178
|
name { "John Doe" }
|
675
|
-
|
1179
|
+
status { :active }
|
676
1180
|
login { "#{name} (M)" }
|
677
1181
|
end
|
678
1182
|
|
679
1183
|
factory :brandon do
|
680
|
-
|
1184
|
+
active
|
681
1185
|
name { "Brandon" }
|
682
1186
|
end
|
683
1187
|
end
|
684
1188
|
```
|
685
1189
|
|
686
|
-
|
1190
|
+
### Using traits
|
1191
|
+
|
1192
|
+
Traits can also be passed in as a list of symbols when you construct an instance
|
1193
|
+
from factory\_bot.
|
687
1194
|
|
688
1195
|
```ruby
|
689
1196
|
factory :user do
|
690
1197
|
name { "Friendly User" }
|
691
1198
|
|
692
|
-
trait :
|
1199
|
+
trait :active do
|
693
1200
|
name { "John Doe" }
|
694
|
-
|
1201
|
+
status { :active }
|
695
1202
|
end
|
696
1203
|
|
697
1204
|
trait :admin do
|
@@ -699,8 +1206,8 @@ factory :user do
|
|
699
1206
|
end
|
700
1207
|
end
|
701
1208
|
|
702
|
-
# creates an admin user with
|
703
|
-
create(:user, :admin, :
|
1209
|
+
# creates an admin user with :active status and name "Jon Snow"
|
1210
|
+
create(:user, :admin, :active, name: "Jon Snow")
|
704
1211
|
```
|
705
1212
|
|
706
1213
|
This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`.
|
@@ -718,10 +1225,12 @@ factory :user do
|
|
718
1225
|
end
|
719
1226
|
end
|
720
1227
|
|
721
|
-
# creates 3 admin users with
|
722
|
-
create_list(:user, 3, :admin, :
|
1228
|
+
# creates 3 admin users with :active status and name "Jon Snow"
|
1229
|
+
create_list(:user, 3, :admin, :active, name: "Jon Snow")
|
723
1230
|
```
|
724
1231
|
|
1232
|
+
### With associations
|
1233
|
+
|
725
1234
|
Traits can be used with associations easily too:
|
726
1235
|
|
727
1236
|
```ruby
|
@@ -762,6 +1271,8 @@ end
|
|
762
1271
|
create(:post).author
|
763
1272
|
```
|
764
1273
|
|
1274
|
+
### Traits within traits
|
1275
|
+
|
765
1276
|
Traits can be used within other traits to mix in their attributes.
|
766
1277
|
|
767
1278
|
```ruby
|
@@ -777,6 +1288,8 @@ factory :order do
|
|
777
1288
|
end
|
778
1289
|
```
|
779
1290
|
|
1291
|
+
### With transient attributes
|
1292
|
+
|
780
1293
|
Finally, traits can accept transient attributes.
|
781
1294
|
|
782
1295
|
```ruby
|
@@ -795,9 +1308,103 @@ end
|
|
795
1308
|
create :invoice, :with_amount, amount: 2
|
796
1309
|
```
|
797
1310
|
|
1311
|
+
### Enum traits
|
1312
|
+
|
1313
|
+
Given an Active Record model with an enum attribute:
|
1314
|
+
|
1315
|
+
```rb
|
1316
|
+
class Task < ActiveRecord::Base
|
1317
|
+
enum status: {queued: 0, started: 1, finished: 2}
|
1318
|
+
end
|
1319
|
+
|
1320
|
+
```
|
1321
|
+
|
1322
|
+
factory\_bot will automatically define traits for each possible value of the
|
1323
|
+
enum:
|
1324
|
+
|
1325
|
+
```rb
|
1326
|
+
FactoryBot.define do
|
1327
|
+
factory :task
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
FactoryBot.build(:task, :queued)
|
1331
|
+
FactoryBot.build(:task, :started)
|
1332
|
+
FactoryBot.build(:task, :finished)
|
1333
|
+
```
|
1334
|
+
|
1335
|
+
Writing the traits out manually would be cumbersome, and is not necessary:
|
1336
|
+
|
1337
|
+
```rb
|
1338
|
+
FactoryBot.define do
|
1339
|
+
factory :task do
|
1340
|
+
trait :queued do
|
1341
|
+
status { :queued }
|
1342
|
+
end
|
1343
|
+
|
1344
|
+
trait :started do
|
1345
|
+
status { :started }
|
1346
|
+
end
|
1347
|
+
|
1348
|
+
trait :finished do
|
1349
|
+
status { :finished }
|
1350
|
+
end
|
1351
|
+
end
|
1352
|
+
end
|
1353
|
+
```
|
1354
|
+
|
1355
|
+
If automatically defining traits for enum attributes on every factory is not
|
1356
|
+
desired, it is possible to disable the feature by setting
|
1357
|
+
`FactoryBot.automatically_define_enum_traits = false`
|
1358
|
+
|
1359
|
+
In that case, it is still possible to explicitly define traits for an enum
|
1360
|
+
attribute in a particular factory:
|
1361
|
+
|
1362
|
+
```rb
|
1363
|
+
FactoryBot.automatically_define_enum_traits = false
|
1364
|
+
|
1365
|
+
FactoryBot.define do
|
1366
|
+
factory :task do
|
1367
|
+
traits_for_enum(:status)
|
1368
|
+
end
|
1369
|
+
end
|
1370
|
+
```
|
1371
|
+
|
1372
|
+
It is also possible to use this feature for other enumerable values, not
|
1373
|
+
specifically tied to Active Record enum attributes.
|
1374
|
+
|
1375
|
+
With an array:
|
1376
|
+
|
1377
|
+
```rb
|
1378
|
+
class Task
|
1379
|
+
attr_accessor :status
|
1380
|
+
end
|
1381
|
+
|
1382
|
+
FactoryBot.define do
|
1383
|
+
factory :task do
|
1384
|
+
traits_for_enum(:status, ["queued", "started", "finished"])
|
1385
|
+
end
|
1386
|
+
end
|
1387
|
+
```
|
1388
|
+
|
1389
|
+
Or with a hash:
|
1390
|
+
|
1391
|
+
```rb
|
1392
|
+
class Task
|
1393
|
+
attr_accessor :status
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
FactoryBot.define do
|
1397
|
+
factory :task do
|
1398
|
+
traits_for_enum(:status, { queued: 0, started: 1, finished: 2 })
|
1399
|
+
end
|
1400
|
+
end
|
1401
|
+
```
|
1402
|
+
|
798
1403
|
Callbacks
|
799
1404
|
---------
|
800
1405
|
|
1406
|
+
### Default callbacks
|
1407
|
+
|
801
1408
|
factory\_bot makes available four callbacks for injecting some code:
|
802
1409
|
|
803
1410
|
* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`)
|
@@ -816,6 +1423,8 @@ end
|
|
816
1423
|
|
817
1424
|
Note that you'll have an instance of the user in the block. This can be useful.
|
818
1425
|
|
1426
|
+
### Multiple callbacks
|
1427
|
+
|
819
1428
|
You can also define multiple types of callbacks on the same factory:
|
820
1429
|
|
821
1430
|
```ruby
|
@@ -825,7 +1434,8 @@ factory :user do
|
|
825
1434
|
end
|
826
1435
|
```
|
827
1436
|
|
828
|
-
Factories can also define any number of the same kind of callback. These
|
1437
|
+
Factories can also define any number of the same kind of callback. These
|
1438
|
+
callbacks will be executed in the order they are specified:
|
829
1439
|
|
830
1440
|
```ruby
|
831
1441
|
factory :user do
|
@@ -836,9 +1446,12 @@ end
|
|
836
1446
|
|
837
1447
|
Calling `create` will invoke both `after_build` and `after_create` callbacks.
|
838
1448
|
|
839
|
-
Also, like standard attributes, child factories will inherit (and can also
|
1449
|
+
Also, like standard attributes, child factories will inherit (and can also
|
1450
|
+
define) callbacks from their parent factory.
|
840
1451
|
|
841
|
-
Multiple callbacks can be assigned to run a block; this is useful when building
|
1452
|
+
Multiple callbacks can be assigned to run a block; this is useful when building
|
1453
|
+
various strategies that run the same code (since there are no callbacks that are
|
1454
|
+
shared across all strategies).
|
842
1455
|
|
843
1456
|
```ruby
|
844
1457
|
factory :user do
|
@@ -848,6 +1461,8 @@ factory :user do
|
|
848
1461
|
end
|
849
1462
|
```
|
850
1463
|
|
1464
|
+
### Global callbacks
|
1465
|
+
|
851
1466
|
To override callbacks for all factories, define them within the
|
852
1467
|
`FactoryBot.define` block:
|
853
1468
|
|
@@ -862,7 +1477,9 @@ FactoryBot.define do
|
|
862
1477
|
end
|
863
1478
|
```
|
864
1479
|
|
865
|
-
|
1480
|
+
### Symbol#to_proc
|
1481
|
+
|
1482
|
+
You can call callbacks that rely on `Symbol#to_proc`:
|
866
1483
|
|
867
1484
|
```ruby
|
868
1485
|
# app/models/user.rb
|
@@ -885,15 +1502,16 @@ create(:user) # creates the user and confirms it
|
|
885
1502
|
Modifying factories
|
886
1503
|
-------------------
|
887
1504
|
|
888
|
-
If you're given a set of factories (say, from a gem developer) but want to
|
889
|
-
|
1505
|
+
If you're given a set of factories (say, from a gem developer) but want to
|
1506
|
+
change them to fit into your application better, you can modify that factory
|
1507
|
+
instead of creating a child factory and adding attributes there.
|
890
1508
|
|
891
1509
|
If a gem were to give you a User factory:
|
892
1510
|
|
893
1511
|
```ruby
|
894
1512
|
FactoryBot.define do
|
895
1513
|
factory :user do
|
896
|
-
full_name "John Doe"
|
1514
|
+
full_name { "John Doe" }
|
897
1515
|
sequence(:username) { |n| "user#{n}" }
|
898
1516
|
password { "password" }
|
899
1517
|
end
|
@@ -907,7 +1525,6 @@ FactoryBot.define do
|
|
907
1525
|
factory :application_user, parent: :user do
|
908
1526
|
full_name { "Jane Doe" }
|
909
1527
|
date_of_birth { 21.years.ago }
|
910
|
-
gender { "Female" }
|
911
1528
|
health { 90 }
|
912
1529
|
end
|
913
1530
|
end
|
@@ -920,7 +1537,6 @@ FactoryBot.modify do
|
|
920
1537
|
factory :user do
|
921
1538
|
full_name { "Jane Doe" }
|
922
1539
|
date_of_birth { 21.years.ago }
|
923
|
-
gender { "Female" }
|
924
1540
|
health { 90 }
|
925
1541
|
end
|
926
1542
|
end
|
@@ -950,6 +1566,14 @@ To set the attributes for each of the factories, you can pass in a hash as you n
|
|
950
1566
|
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
|
951
1567
|
```
|
952
1568
|
|
1569
|
+
In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters:
|
1570
|
+
|
1571
|
+
```ruby
|
1572
|
+
twenty_somethings = build_list(:user, 10) do |user, i|
|
1573
|
+
user.date_of_birth = (20 + i).years.ago
|
1574
|
+
end
|
1575
|
+
```
|
1576
|
+
|
953
1577
|
`build_stubbed_list` will give you fully stubbed out instances:
|
954
1578
|
|
955
1579
|
```ruby
|
@@ -972,7 +1596,7 @@ users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
|
|
972
1596
|
Linting Factories
|
973
1597
|
-----------------
|
974
1598
|
|
975
|
-
|
1599
|
+
factory\_bot allows for linting known factories:
|
976
1600
|
|
977
1601
|
```ruby
|
978
1602
|
FactoryBot.lint
|
@@ -999,8 +1623,10 @@ namespace :factory_bot do
|
|
999
1623
|
desc "Verify that all FactoryBot factories are valid"
|
1000
1624
|
task lint: :environment do
|
1001
1625
|
if Rails.env.test?
|
1002
|
-
|
1626
|
+
conn = ActiveRecord::Base.connection
|
1627
|
+
conn.transaction do
|
1003
1628
|
FactoryBot.lint
|
1629
|
+
raise ActiveRecord::Rollback
|
1004
1630
|
end
|
1005
1631
|
else
|
1006
1632
|
system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
|
@@ -1012,8 +1638,7 @@ end
|
|
1012
1638
|
|
1013
1639
|
After calling `FactoryBot.lint`, you'll likely want to clear out the
|
1014
1640
|
database, as records will most likely be created. The provided example above
|
1015
|
-
uses
|
1016
|
-
gem to your Gemfile under the appropriate groups.
|
1641
|
+
uses an sql transaction and rollback to leave the database clean.
|
1017
1642
|
|
1018
1643
|
You can lint factories selectively by passing only factories you want linted:
|
1019
1644
|
|
@@ -1047,10 +1672,17 @@ You can also specify the strategy used for linting:
|
|
1047
1672
|
FactoryBot.lint strategy: :build
|
1048
1673
|
```
|
1049
1674
|
|
1675
|
+
Verbose linting will include full backtraces for each error, which can be
|
1676
|
+
helpful for debugging:
|
1677
|
+
|
1678
|
+
```ruby
|
1679
|
+
FactoryBot.lint verbose: true
|
1680
|
+
```
|
1681
|
+
|
1050
1682
|
Custom Construction
|
1051
1683
|
-------------------
|
1052
1684
|
|
1053
|
-
If you want to use
|
1685
|
+
If you want to use factory\_bot to construct an object where some attributes
|
1054
1686
|
are passed to `initialize` or if you want to do something other than simply
|
1055
1687
|
calling `new` on your build class, you can override the default behavior by
|
1056
1688
|
defining `initialize_with` on your factory. Example:
|
@@ -1078,7 +1710,7 @@ end
|
|
1078
1710
|
build(:user).name # Jane Doe
|
1079
1711
|
```
|
1080
1712
|
|
1081
|
-
Although
|
1713
|
+
Although factory\_bot is written to work with ActiveRecord out of the box, it
|
1082
1714
|
can also work with any Ruby class. For maximum compatibility with ActiveRecord,
|
1083
1715
|
the default initializer builds all instances by calling `new` on your build class
|
1084
1716
|
without any arguments. It then calls attribute writer methods to assign all the
|
@@ -1089,7 +1721,7 @@ You can override the initializer in order to:
|
|
1089
1721
|
|
1090
1722
|
* Build non-ActiveRecord objects that require arguments to `initialize`
|
1091
1723
|
* Use a method other than `new` to instantiate the instance
|
1092
|
-
* Do
|
1724
|
+
* Do wild things like decorate the instance after it's built
|
1093
1725
|
|
1094
1726
|
When using `initialize_with`, you don't have to declare the class itself when
|
1095
1727
|
calling `new`; however, any other class methods you want to call will have to
|
@@ -1116,7 +1748,7 @@ factory :user do
|
|
1116
1748
|
|
1117
1749
|
name "John Doe"
|
1118
1750
|
|
1119
|
-
initialize_with { new(attributes) }
|
1751
|
+
initialize_with { new(**attributes) }
|
1120
1752
|
end
|
1121
1753
|
```
|
1122
1754
|
|
@@ -1151,7 +1783,7 @@ build(:user)
|
|
1151
1783
|
User.new('value')
|
1152
1784
|
```
|
1153
1785
|
|
1154
|
-
This prevents duplicate assignment; in versions of
|
1786
|
+
This prevents duplicate assignment; in versions of factory\_bot before 4.0, it
|
1155
1787
|
would run this:
|
1156
1788
|
|
1157
1789
|
```ruby
|
@@ -1337,12 +1969,12 @@ with associations, as below:
|
|
1337
1969
|
|
1338
1970
|
```ruby
|
1339
1971
|
FactoryBot.define do
|
1340
|
-
factory :united_states, class: Location do
|
1972
|
+
factory :united_states, class: "Location" do
|
1341
1973
|
name { 'United States' }
|
1342
1974
|
association :location_group, factory: :north_america
|
1343
1975
|
end
|
1344
1976
|
|
1345
|
-
factory :north_america, class: LocationGroup do
|
1977
|
+
factory :north_america, class: "LocationGroup" do
|
1346
1978
|
name { 'North America' }
|
1347
1979
|
end
|
1348
1980
|
end
|