factory_bot 5.0.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 +705 -149
- data/NEWS.md +51 -1
- data/README.md +19 -18
- data/lib/factory_bot.rb +21 -93
- 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_assigner.rb +8 -9
- data/lib/factory_bot/attribute_list.rb +1 -1
- data/lib/factory_bot/callback.rb +3 -11
- data/lib/factory_bot/configuration.rb +6 -6
- data/lib/factory_bot/declaration.rb +1 -1
- data/lib/factory_bot/declaration/association.rb +30 -2
- data/lib/factory_bot/declaration/implicit.rb +4 -1
- data/lib/factory_bot/declaration_list.rb +2 -2
- data/lib/factory_bot/decorator.rb +18 -6
- data/lib/factory_bot/decorator/invocation_tracker.rb +2 -1
- data/lib/factory_bot/definition.rb +66 -19
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +77 -12
- data/lib/factory_bot/enum.rb +27 -0
- data/lib/factory_bot/errors.rb +3 -0
- data/lib/factory_bot/evaluator.rb +7 -8
- data/lib/factory_bot/evaluator_class_definer.rb +1 -1
- data/lib/factory_bot/factory.rb +13 -13
- data/lib/factory_bot/factory_runner.rb +4 -4
- data/lib/factory_bot/find_definitions.rb +1 -1
- data/lib/factory_bot/internal.rb +68 -1
- data/lib/factory_bot/linter.rb +9 -13
- data/lib/factory_bot/null_factory.rb +10 -4
- data/lib/factory_bot/null_object.rb +2 -6
- data/lib/factory_bot/registry.rb +4 -4
- data/lib/factory_bot/reload.rb +1 -2
- data/lib/factory_bot/sequence.rb +5 -5
- data/lib/factory_bot/strategy/null.rb +4 -2
- data/lib/factory_bot/strategy/stub.rb +16 -5
- data/lib/factory_bot/strategy_calculator.rb +1 -1
- data/lib/factory_bot/strategy_syntax_method_registrar.rb +12 -1
- data/lib/factory_bot/syntax/default.rb +12 -24
- data/lib/factory_bot/syntax/methods.rb +3 -3
- data/lib/factory_bot/trait.rb +6 -4
- data/lib/factory_bot/version.rb +1 -1
- metadata +12 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a928c94babd835a0756175aa3d7a1f03d4dae15b0782818a8c9688489f6e18d3
|
4
|
+
data.tar.gz: e2875eca60137531b83267dcdfd87ba1329ed8a52e460a0e609fa5fb04f39244
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf8f7006ff4e0f34533259c6e747bb61cd8ebcd6d68b7a9076b811bf4b152662997f18ad467953c07f3a0c7c10585b48b4f0489ce6aa1a16b42ad02641e7169b
|
7
|
+
data.tar.gz: a80c3ed49517c2cee0dc7bdafc9dd3459d7fcd83443873e2a8c72db8a3c96b6d902975c8a586ecbfb1cd6b8dab9513a4bdfaa22f84c297f81b2736a8f4317e8d
|
data/CONTRIBUTING.md
CHANGED
@@ -12,7 +12,7 @@ Here are some ways *you* can contribute:
|
|
12
12
|
* by suggesting new features
|
13
13
|
* by writing or editing documentation
|
14
14
|
* by writing specifications
|
15
|
-
* by writing code ( **no patch is too small** : fix typos, add comments,
|
15
|
+
* by writing code ( **no patch is too small** : fix typos, add comments, etc. )
|
16
16
|
* by refactoring code
|
17
17
|
* by closing [issues][]
|
18
18
|
* by reviewing patches
|
@@ -23,13 +23,10 @@ Here are some ways *you* can contribute:
|
|
23
23
|
|
24
24
|
* We use the [GitHub issue tracker][issues] to track bugs and features.
|
25
25
|
* Before submitting a bug report or feature request, check to make sure it hasn't
|
26
|
-
already been submitted.
|
27
|
-
* When submitting a bug report, please include a [
|
28
|
-
|
29
|
-
|
30
|
-
should include a pull request with failing specs.
|
31
|
-
|
32
|
-
[gist]: https://gist.github.com/
|
26
|
+
already been submitted.
|
27
|
+
* When submitting a bug report, please include a [reproduction script] and any
|
28
|
+
other details that may be necessary to reproduce the bug, including your gem
|
29
|
+
version, Ruby version, and operating system.
|
33
30
|
|
34
31
|
## Cleaning up issues
|
35
32
|
|
@@ -40,21 +37,69 @@ already been submitted.
|
|
40
37
|
We will happily reopen the issue.
|
41
38
|
|
42
39
|
## Submitting a Pull Request
|
40
|
+
|
43
41
|
1. [Fork][fork] the [official repository][repo].
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
42
|
+
1. [Create a topic branch.][branch]
|
43
|
+
1. Implement your feature or bug fix.
|
44
|
+
1. Add, commit, and push your changes.
|
45
|
+
1. [Submit a pull request.][pr]
|
46
|
+
|
47
|
+
### Notes
|
48
48
|
|
49
|
-
## Notes
|
50
49
|
* Please add tests if you changed code. Contributions without tests won't be accepted.
|
51
50
|
* If you don't know how to add tests, please put in a PR and leave a comment
|
52
51
|
asking for help. We love helping!
|
53
52
|
* Please don't update the Gem version.
|
54
53
|
|
54
|
+
## Setting up
|
55
|
+
|
56
|
+
```sh
|
57
|
+
bundle install
|
58
|
+
```
|
59
|
+
|
60
|
+
## Running the test suite
|
61
|
+
|
62
|
+
The default rake task will run the full test suite and [standard]:
|
63
|
+
|
64
|
+
```sh
|
65
|
+
bundle exec rake
|
66
|
+
```
|
67
|
+
|
68
|
+
You can also run a single group of tests (unit, spec, or feature)
|
69
|
+
|
70
|
+
```sh
|
71
|
+
bundle exec rake spec:unit
|
72
|
+
bundle exec rake spec:acceptance
|
73
|
+
bundle exec rake features
|
74
|
+
```
|
75
|
+
|
76
|
+
To run an individual rspec test, you can provide a path and line number:
|
77
|
+
|
78
|
+
```sh
|
79
|
+
bundle exec rspec spec/path/to/spec.rb:123
|
80
|
+
```
|
81
|
+
|
82
|
+
You can run tests with a specific version of rails via [appraisal]. To run
|
83
|
+
the default rake task against Rails 6, for example:
|
84
|
+
|
85
|
+
```sh
|
86
|
+
bundle exec appraisal 6.0 rake
|
87
|
+
```
|
88
|
+
|
89
|
+
## Formatting
|
90
|
+
|
91
|
+
Use [standard] to automatically format your code:
|
92
|
+
|
93
|
+
```sh
|
94
|
+
bundle exec rake standard:fix
|
95
|
+
```
|
96
|
+
|
55
97
|
[repo]: https://github.com/thoughtbot/factory_bot/tree/master
|
56
98
|
[fork]: https://help.github.com/articles/fork-a-repo/
|
57
99
|
[branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/
|
58
100
|
[pr]: https://help.github.com/articles/using-pull-requests/
|
101
|
+
[standard]: https://github.com/testdouble/standard
|
102
|
+
[appraisal]: https://github.com/thoughtbot/appraisal
|
103
|
+
[reproduction script]: https://github.com/thoughtbot/factory_bot/blob/master/.github/REPRODUCTION_SCRIPT.rb
|
59
104
|
|
60
105
|
Inspired by https://github.com/middleman/middleman-heroku/blob/master/CONTRIBUTING.md
|
data/GETTING_STARTED.md
CHANGED
@@ -1,8 +1,91 @@
|
|
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
90
|
If you're using Rails:
|
8
91
|
|
@@ -16,21 +99,13 @@ If you're *not* using Rails:
|
|
16
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:
|
21
|
-
|
22
|
-
```bash
|
23
|
-
export JRUBY_OPTS=--1.9
|
24
|
-
```
|
25
|
-
|
26
|
-
Once your Gemfile is updated, you'll want to update your bundle.
|
27
|
-
|
28
|
-
Configure your test suite
|
29
|
-
-------------------------
|
102
|
+
### Configure your test suite
|
30
103
|
|
31
|
-
|
104
|
+
#### RSpec
|
32
105
|
|
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
|
@@ -115,23 +194,25 @@ FactoryBot.define do
|
|
115
194
|
end
|
116
195
|
```
|
117
196
|
|
197
|
+
### Specifying the class explicitly
|
198
|
+
|
118
199
|
It is also possible to explicitly specify the class:
|
119
200
|
|
120
201
|
```ruby
|
121
202
|
# This will use the User class (otherwise Admin would have been guessed)
|
122
|
-
factory :admin, class: User
|
203
|
+
factory :admin, class: "User"
|
123
204
|
```
|
124
205
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
which factory_bot will constantize later, once you start building objects:
|
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).
|
129
209
|
|
130
210
|
```ruby
|
131
|
-
|
132
|
-
factory :access_token, class: "Doorkeeper::AccessToken"
|
211
|
+
factory :access_token, class: User
|
133
212
|
```
|
134
213
|
|
214
|
+
### Hash attributes
|
215
|
+
|
135
216
|
Because of the block syntax in Ruby, defining attributes as `Hash`es (for
|
136
217
|
serialized/JSON columns, for example) requires two sets of curly brackets:
|
137
218
|
|
@@ -141,10 +222,19 @@ factory :program do
|
|
141
222
|
end
|
142
223
|
```
|
143
224
|
|
144
|
-
|
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.
|
145
233
|
|
146
234
|
Attempting to define multiple factories with the same name will raise an error.
|
147
235
|
|
236
|
+
### Definition file paths
|
237
|
+
|
148
238
|
Factories can be defined anywhere, but will be automatically loaded after
|
149
239
|
calling `FactoryBot.find_definitions` if factories are defined in files at the
|
150
240
|
following locations:
|
@@ -154,10 +244,20 @@ following locations:
|
|
154
244
|
test/factories/*.rb
|
155
245
|
spec/factories/*.rb
|
156
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
|
+
|
157
254
|
Using factories
|
158
255
|
---------------
|
159
256
|
|
160
|
-
|
257
|
+
### Build strategies
|
258
|
+
|
259
|
+
factory\_bot supports several different build strategies: build, create,
|
260
|
+
attributes\_for and build\_stubbed:
|
161
261
|
|
162
262
|
```ruby
|
163
263
|
# Returns a User instance that's not saved
|
@@ -178,7 +278,10 @@ create(:user) do |user|
|
|
178
278
|
end
|
179
279
|
```
|
180
280
|
|
181
|
-
|
281
|
+
### Attribute overrides
|
282
|
+
|
283
|
+
No matter which strategy is used, it's possible to override the defined
|
284
|
+
attributes by passing a hash:
|
182
285
|
|
183
286
|
```ruby
|
184
287
|
# Build a User instance and override the first_name property
|
@@ -187,19 +290,20 @@ user.first_name
|
|
187
290
|
# => "Joe"
|
188
291
|
```
|
189
292
|
|
190
|
-
|
191
|
-
`Marshal.dump`, since factory_bot defines singleton methods on these objects.
|
192
|
-
|
193
|
-
Static Attributes
|
194
|
-
------------------
|
293
|
+
### `build_stubbed` and `Marshal.dump`
|
195
294
|
|
196
|
-
|
197
|
-
|
198
|
-
[this blog post](https://robots.thoughtbot.com/deprecating-static-attributes-in-factory_bot-4-11).
|
295
|
+
Note that objects created with `build_stubbed` cannot be serialized with
|
296
|
+
`Marshal.dump`, since factory\_bot defines singleton methods on these objects.
|
199
297
|
|
200
298
|
Aliases
|
201
299
|
-------
|
202
|
-
|
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.
|
203
307
|
|
204
308
|
```ruby
|
205
309
|
factory :user, aliases: [:author, :commenter] do
|
@@ -209,17 +313,17 @@ factory :user, aliases: [:author, :commenter] do
|
|
209
313
|
end
|
210
314
|
|
211
315
|
factory :post do
|
212
|
-
author
|
213
|
-
# instead of
|
316
|
+
# The alias allows us to write author instead of
|
214
317
|
# association :author, factory: :user
|
318
|
+
author
|
215
319
|
title { "How to read a book effectively" }
|
216
320
|
body { "There are five steps involved." }
|
217
321
|
end
|
218
322
|
|
219
323
|
factory :comment do
|
220
|
-
commenter
|
221
|
-
# instead of
|
324
|
+
# The alias allows us to write commenter instead of
|
222
325
|
# association :commenter, factory: :user
|
326
|
+
commenter
|
223
327
|
body { "Great article!" }
|
224
328
|
end
|
225
329
|
```
|
@@ -243,36 +347,79 @@ create(:user, last_name: "Doe").email
|
|
243
347
|
|
244
348
|
Transient Attributes
|
245
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
|
246
353
|
|
247
|
-
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)):
|
248
357
|
|
249
358
|
```ruby
|
250
359
|
factory :user do
|
251
360
|
transient do
|
252
361
|
rockstar { true }
|
253
|
-
upcased { false }
|
254
362
|
end
|
255
363
|
|
256
364
|
name { "John Doe#{" - Rockstar" if rockstar}" }
|
257
|
-
|
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" }
|
258
392
|
|
259
393
|
after(:create) do |user, evaluator|
|
260
394
|
user.name.upcase! if evaluator.upcased
|
261
395
|
end
|
262
396
|
end
|
263
397
|
|
398
|
+
create(:user).name
|
399
|
+
#=> "John Doe"
|
400
|
+
|
264
401
|
create(:user, upcased: true).name
|
265
|
-
#=> "JOHN DOE
|
402
|
+
#=> "JOHN DOE"
|
266
403
|
```
|
267
404
|
|
268
|
-
|
269
|
-
set on the model,
|
270
|
-
even if the attribute exists or you attempt to override it.
|
405
|
+
### With associations
|
271
406
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
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
|
+
```
|
276
423
|
|
277
424
|
Method Name / Reserved Word Attributes
|
278
425
|
-------------------------------
|
@@ -293,7 +440,10 @@ end
|
|
293
440
|
Inheritance
|
294
441
|
-----------
|
295
442
|
|
296
|
-
|
443
|
+
### Nested factories
|
444
|
+
|
445
|
+
You can easily create multiple factories for the same class without repeating
|
446
|
+
common attributes by nesting factories:
|
297
447
|
|
298
448
|
```ruby
|
299
449
|
factory :post do
|
@@ -309,6 +459,8 @@ approved_post.title # => "A title"
|
|
309
459
|
approved_post.approved # => true
|
310
460
|
```
|
311
461
|
|
462
|
+
### Assigning parent explicitly
|
463
|
+
|
312
464
|
You can also assign the parent explicitly:
|
313
465
|
|
314
466
|
```ruby
|
@@ -321,6 +473,8 @@ factory :approved_post, parent: :post do
|
|
321
473
|
end
|
322
474
|
```
|
323
475
|
|
476
|
+
### Best practices
|
477
|
+
|
324
478
|
As mentioned above, it's good practice to define a basic factory for each class
|
325
479
|
with only the attributes required to create it. Then, create more specific
|
326
480
|
factories that inherit from this basic parent. Factory definitions are still
|
@@ -329,7 +483,10 @@ code, so keep them DRY.
|
|
329
483
|
Associations
|
330
484
|
------------
|
331
485
|
|
332
|
-
|
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.
|
333
490
|
|
334
491
|
```ruby
|
335
492
|
factory :post do
|
@@ -338,15 +495,117 @@ factory :post do
|
|
338
495
|
end
|
339
496
|
```
|
340
497
|
|
341
|
-
|
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:
|
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:
|
342
560
|
|
343
561
|
```ruby
|
344
562
|
factory :post do
|
345
563
|
# ...
|
346
|
-
|
564
|
+
author factory: :author, last_name: "Writely"
|
347
565
|
end
|
348
566
|
```
|
349
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
|
+
|
350
609
|
In factory\_bot 5, associations default to using the same build strategy as
|
351
610
|
their parent object:
|
352
611
|
|
@@ -387,7 +646,7 @@ post.new_record? # => true
|
|
387
646
|
post.author.new_record? # => false
|
388
647
|
```
|
389
648
|
|
390
|
-
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:
|
391
650
|
|
392
651
|
```ruby
|
393
652
|
FactoryBot.use_parent_strategy = false
|
@@ -412,27 +671,53 @@ factory :post do
|
|
412
671
|
author strategy: :build # <<< this does *not* work; causes author_id to be nil
|
413
672
|
```
|
414
673
|
|
415
|
-
|
416
|
-
|
417
|
-
|
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:
|
418
679
|
|
419
680
|
```ruby
|
420
681
|
FactoryBot.define do
|
682
|
+
factory :post do
|
683
|
+
title { "Through the Looking Glass" }
|
684
|
+
user
|
685
|
+
end
|
421
686
|
|
422
|
-
|
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
|
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
|
423
709
|
factory :post do
|
424
710
|
title { "Through the Looking Glass" }
|
425
711
|
user
|
426
712
|
end
|
427
713
|
|
428
|
-
# user factory without associated posts
|
429
714
|
factory :user do
|
430
715
|
name { "John Doe" }
|
431
716
|
|
432
717
|
# user_with_posts will create post data after the user has been created
|
433
718
|
factory :user_with_posts do
|
434
|
-
# posts_count is declared as a transient attribute
|
435
|
-
#
|
719
|
+
# posts_count is declared as a transient attribute available in the
|
720
|
+
# callback via the evaluator
|
436
721
|
transient do
|
437
722
|
posts_count { 5 }
|
438
723
|
end
|
@@ -443,87 +728,142 @@ FactoryBot.define do
|
|
443
728
|
# to create and we make sure the user is associated properly to the post
|
444
729
|
after(:create) do |user, evaluator|
|
445
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
|
446
734
|
end
|
447
735
|
end
|
448
736
|
end
|
449
737
|
end
|
450
|
-
```
|
451
|
-
|
452
|
-
This allows us to do:
|
453
738
|
|
454
|
-
```ruby
|
455
739
|
create(:user).posts.length # 0
|
456
740
|
create(:user_with_posts).posts.length # 5
|
457
741
|
create(:user_with_posts, posts_count: 15).posts.length # 15
|
458
742
|
```
|
459
743
|
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
object to the singular version of the attribute name.
|
464
|
-
|
465
|
-
Here's an example with two models that are related via
|
466
|
-
`has_and_belongs_to_many`:
|
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:
|
467
747
|
|
468
748
|
```ruby
|
469
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" }
|
470
757
|
|
471
|
-
|
472
|
-
|
758
|
+
factory :user_with_posts do
|
759
|
+
posts { [association(:post)] }
|
760
|
+
end
|
761
|
+
end
|
762
|
+
end
|
763
|
+
|
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
|
473
776
|
title { "Through the Looking Glass" }
|
474
|
-
|
777
|
+
user
|
475
778
|
end
|
476
779
|
|
477
|
-
|
478
|
-
|
479
|
-
name { "John Doe" }
|
780
|
+
factory :user do
|
781
|
+
name { "Adiza Kumato" }
|
480
782
|
|
481
|
-
|
482
|
-
# been created
|
483
|
-
factory :profile_with_languages do
|
484
|
-
# languages_count is declared as an ignored attribute and available in
|
485
|
-
# attributes on the factory, as well as the callback via the evaluator
|
783
|
+
factory :user_with_posts do
|
486
784
|
transient do
|
487
|
-
|
785
|
+
posts_count { 5 }
|
488
786
|
end
|
489
787
|
|
490
|
-
|
491
|
-
|
492
|
-
# ignored attributes; `create_list`'s second argument is the number of
|
493
|
-
# records to create and we make sure the profile is associated properly
|
494
|
-
# to the language
|
495
|
-
after(:create) do |profile, evaluator|
|
496
|
-
create_list(:language, evaluator.languages_count, profiles: [profile])
|
788
|
+
posts do
|
789
|
+
Array.new(posts_count) { association(:post) }
|
497
790
|
end
|
498
791
|
end
|
499
792
|
end
|
500
793
|
end
|
794
|
+
|
795
|
+
create(:user_with_posts).posts.length # 5
|
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
|
501
799
|
```
|
502
800
|
|
503
|
-
|
801
|
+
### `has_and_belongs_to_many` associations
|
802
|
+
|
803
|
+
Generating data for a `has_and_belongs_to_many` relationship is very similar
|
804
|
+
to the above `has_many` relationship, with a small change: you need to pass an
|
805
|
+
array of objects to the model's pluralized attribute name rather than a single
|
806
|
+
object to the singular version of the attribute name.
|
807
|
+
|
504
808
|
|
505
809
|
```ruby
|
506
|
-
|
507
|
-
create(:
|
508
|
-
|
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
|
509
815
|
```
|
510
816
|
|
511
|
-
|
817
|
+
Or with the callback approach:
|
818
|
+
|
819
|
+
```ruby
|
820
|
+
factory :profile_with_languages do
|
821
|
+
transient do
|
822
|
+
languages_count { 2 }
|
823
|
+
end
|
512
824
|
|
825
|
+
after(:create) do |profile, evaluator|
|
826
|
+
create_list(:language, evaluator.languages_count, profiles: [profile])
|
827
|
+
profile.reload
|
828
|
+
end
|
829
|
+
end
|
513
830
|
```
|
831
|
+
|
832
|
+
Or the inline association approach (note the use of the `instance` method here
|
833
|
+
to refer to the profile being built):
|
834
|
+
|
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
|
514
854
|
FactoryBot.define do
|
515
855
|
factory :video
|
516
856
|
factory :photo
|
517
857
|
|
518
858
|
factory :comment do
|
519
|
-
for_photo
|
859
|
+
for_photo # default to the :for_photo trait if none is specified
|
520
860
|
|
521
861
|
trait :for_video do
|
522
|
-
association
|
862
|
+
association :commentable, factory: :video
|
523
863
|
end
|
524
864
|
|
525
865
|
trait :for_photo do
|
526
|
-
association
|
866
|
+
association :commentable, factory: :photo
|
527
867
|
end
|
528
868
|
end
|
529
869
|
end
|
@@ -531,16 +871,73 @@ end
|
|
531
871
|
|
532
872
|
This allows us to do:
|
533
873
|
|
534
|
-
```
|
874
|
+
```ruby
|
535
875
|
create(:comment)
|
536
876
|
create(:comment, :for_video)
|
537
877
|
create(:comment, :for_photo)
|
538
878
|
```
|
539
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`.
|
540
935
|
|
541
936
|
Sequences
|
542
937
|
---------
|
543
938
|
|
939
|
+
### Global sequences
|
940
|
+
|
544
941
|
Unique values in a specific format (for example, e-mail addresses) can be
|
545
942
|
generated using sequences. Sequences are defined by calling `sequence` in a
|
546
943
|
definition block, and values in a sequence are generated by calling
|
@@ -561,6 +958,8 @@ generate :email
|
|
561
958
|
# => "person2@example.com"
|
562
959
|
```
|
563
960
|
|
961
|
+
### With dynamic attributes
|
962
|
+
|
564
963
|
Sequences can be used in dynamic attributes:
|
565
964
|
|
566
965
|
```ruby
|
@@ -569,6 +968,8 @@ factory :invite do
|
|
569
968
|
end
|
570
969
|
```
|
571
970
|
|
971
|
+
### As implicit attributes
|
972
|
+
|
572
973
|
Or as implicit attributes:
|
573
974
|
|
574
975
|
```ruby
|
@@ -580,6 +981,8 @@ end
|
|
580
981
|
Note that defining sequences as implicit attributes will not work if you have a
|
581
982
|
factory with the same name as the sequence.
|
582
983
|
|
984
|
+
### Inline sequences
|
985
|
+
|
583
986
|
And it's also possible to define an in-line sequence that is only used in
|
584
987
|
a particular factory:
|
585
988
|
|
@@ -589,7 +992,10 @@ factory :user do
|
|
589
992
|
end
|
590
993
|
```
|
591
994
|
|
592
|
-
|
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')
|
593
999
|
|
594
1000
|
```ruby
|
595
1001
|
factory :user do
|
@@ -597,6 +1003,8 @@ factory :user do
|
|
597
1003
|
end
|
598
1004
|
```
|
599
1005
|
|
1006
|
+
### Without a block
|
1007
|
+
|
600
1008
|
Without a block, the value will increment itself, starting at its initial value:
|
601
1009
|
|
602
1010
|
```ruby
|
@@ -605,6 +1013,8 @@ factory :post do
|
|
605
1013
|
end
|
606
1014
|
```
|
607
1015
|
|
1016
|
+
### Aliases
|
1017
|
+
|
608
1018
|
Sequences can also have aliases. The sequence aliases share the same counter:
|
609
1019
|
|
610
1020
|
```ruby
|
@@ -634,6 +1044,8 @@ end
|
|
634
1044
|
|
635
1045
|
The value just needs to support the `#next` method. Here the next value will be 'a', then 'b', etc.
|
636
1046
|
|
1047
|
+
### Rewinding
|
1048
|
+
|
637
1049
|
Sequences can also be rewound with `FactoryBot.rewind_sequences`:
|
638
1050
|
|
639
1051
|
```ruby
|
@@ -650,9 +1062,27 @@ generate(:email) # "person1@example.com"
|
|
650
1062
|
|
651
1063
|
This rewinds all registered sequences.
|
652
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
|
+
|
653
1081
|
Traits
|
654
1082
|
------
|
655
1083
|
|
1084
|
+
### Defining traits
|
1085
|
+
|
656
1086
|
Traits allow you to group attributes together and then apply them
|
657
1087
|
to any factory.
|
658
1088
|
|
@@ -688,6 +1118,8 @@ factory :story do
|
|
688
1118
|
end
|
689
1119
|
```
|
690
1120
|
|
1121
|
+
### As implicit attributes
|
1122
|
+
|
691
1123
|
Traits can be used as implicit attributes:
|
692
1124
|
|
693
1125
|
```ruby
|
@@ -701,6 +1133,8 @@ end
|
|
701
1133
|
Note that defining traits as implicit attributes will not work if you have a
|
702
1134
|
factory or sequence with the same name as the trait.
|
703
1135
|
|
1136
|
+
### Attribute precedence
|
1137
|
+
|
704
1138
|
Traits that define the same attributes won't raise AttributeDefinitionErrors;
|
705
1139
|
the trait that defines the attribute latest gets precedence.
|
706
1140
|
|
@@ -709,16 +1143,16 @@ factory :user do
|
|
709
1143
|
name { "Friendly User" }
|
710
1144
|
login { name }
|
711
1145
|
|
712
|
-
trait :
|
1146
|
+
trait :active do
|
713
1147
|
name { "John Doe" }
|
714
|
-
|
715
|
-
login { "#{name} (
|
1148
|
+
status { :active }
|
1149
|
+
login { "#{name} (active)" }
|
716
1150
|
end
|
717
1151
|
|
718
|
-
trait :
|
1152
|
+
trait :inactive do
|
719
1153
|
name { "Jane Doe" }
|
720
|
-
|
721
|
-
login { "#{name} (
|
1154
|
+
status { :inactive }
|
1155
|
+
login { "#{name} (inactive)" }
|
722
1156
|
end
|
723
1157
|
|
724
1158
|
trait :admin do
|
@@ -726,40 +1160,45 @@ factory :user do
|
|
726
1160
|
login { "admin-#{name}" }
|
727
1161
|
end
|
728
1162
|
|
729
|
-
factory :
|
730
|
-
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)"
|
731
1165
|
end
|
732
1166
|
```
|
733
1167
|
|
734
|
-
|
1168
|
+
### In child factories
|
1169
|
+
|
1170
|
+
You can override individual attributes granted by a trait in a child factory:
|
735
1171
|
|
736
1172
|
```ruby
|
737
1173
|
factory :user do
|
738
1174
|
name { "Friendly User" }
|
739
1175
|
login { name }
|
740
1176
|
|
741
|
-
trait :
|
1177
|
+
trait :active do
|
742
1178
|
name { "John Doe" }
|
743
|
-
|
1179
|
+
status { :active }
|
744
1180
|
login { "#{name} (M)" }
|
745
1181
|
end
|
746
1182
|
|
747
1183
|
factory :brandon do
|
748
|
-
|
1184
|
+
active
|
749
1185
|
name { "Brandon" }
|
750
1186
|
end
|
751
1187
|
end
|
752
1188
|
```
|
753
1189
|
|
754
|
-
|
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.
|
755
1194
|
|
756
1195
|
```ruby
|
757
1196
|
factory :user do
|
758
1197
|
name { "Friendly User" }
|
759
1198
|
|
760
|
-
trait :
|
1199
|
+
trait :active do
|
761
1200
|
name { "John Doe" }
|
762
|
-
|
1201
|
+
status { :active }
|
763
1202
|
end
|
764
1203
|
|
765
1204
|
trait :admin do
|
@@ -767,8 +1206,8 @@ factory :user do
|
|
767
1206
|
end
|
768
1207
|
end
|
769
1208
|
|
770
|
-
# creates an admin user with
|
771
|
-
create(:user, :admin, :
|
1209
|
+
# creates an admin user with :active status and name "Jon Snow"
|
1210
|
+
create(:user, :admin, :active, name: "Jon Snow")
|
772
1211
|
```
|
773
1212
|
|
774
1213
|
This ability works with `build`, `build_stubbed`, `attributes_for`, and `create`.
|
@@ -786,10 +1225,12 @@ factory :user do
|
|
786
1225
|
end
|
787
1226
|
end
|
788
1227
|
|
789
|
-
# creates 3 admin users with
|
790
|
-
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")
|
791
1230
|
```
|
792
1231
|
|
1232
|
+
### With associations
|
1233
|
+
|
793
1234
|
Traits can be used with associations easily too:
|
794
1235
|
|
795
1236
|
```ruby
|
@@ -830,6 +1271,8 @@ end
|
|
830
1271
|
create(:post).author
|
831
1272
|
```
|
832
1273
|
|
1274
|
+
### Traits within traits
|
1275
|
+
|
833
1276
|
Traits can be used within other traits to mix in their attributes.
|
834
1277
|
|
835
1278
|
```ruby
|
@@ -845,6 +1288,8 @@ factory :order do
|
|
845
1288
|
end
|
846
1289
|
```
|
847
1290
|
|
1291
|
+
### With transient attributes
|
1292
|
+
|
848
1293
|
Finally, traits can accept transient attributes.
|
849
1294
|
|
850
1295
|
```ruby
|
@@ -863,9 +1308,103 @@ end
|
|
863
1308
|
create :invoice, :with_amount, amount: 2
|
864
1309
|
```
|
865
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
|
+
|
866
1403
|
Callbacks
|
867
1404
|
---------
|
868
1405
|
|
1406
|
+
### Default callbacks
|
1407
|
+
|
869
1408
|
factory\_bot makes available four callbacks for injecting some code:
|
870
1409
|
|
871
1410
|
* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`)
|
@@ -884,6 +1423,8 @@ end
|
|
884
1423
|
|
885
1424
|
Note that you'll have an instance of the user in the block. This can be useful.
|
886
1425
|
|
1426
|
+
### Multiple callbacks
|
1427
|
+
|
887
1428
|
You can also define multiple types of callbacks on the same factory:
|
888
1429
|
|
889
1430
|
```ruby
|
@@ -893,7 +1434,8 @@ factory :user do
|
|
893
1434
|
end
|
894
1435
|
```
|
895
1436
|
|
896
|
-
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:
|
897
1439
|
|
898
1440
|
```ruby
|
899
1441
|
factory :user do
|
@@ -904,9 +1446,12 @@ end
|
|
904
1446
|
|
905
1447
|
Calling `create` will invoke both `after_build` and `after_create` callbacks.
|
906
1448
|
|
907
|
-
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.
|
908
1451
|
|
909
|
-
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).
|
910
1455
|
|
911
1456
|
```ruby
|
912
1457
|
factory :user do
|
@@ -916,6 +1461,8 @@ factory :user do
|
|
916
1461
|
end
|
917
1462
|
```
|
918
1463
|
|
1464
|
+
### Global callbacks
|
1465
|
+
|
919
1466
|
To override callbacks for all factories, define them within the
|
920
1467
|
`FactoryBot.define` block:
|
921
1468
|
|
@@ -930,7 +1477,9 @@ FactoryBot.define do
|
|
930
1477
|
end
|
931
1478
|
```
|
932
1479
|
|
933
|
-
|
1480
|
+
### Symbol#to_proc
|
1481
|
+
|
1482
|
+
You can call callbacks that rely on `Symbol#to_proc`:
|
934
1483
|
|
935
1484
|
```ruby
|
936
1485
|
# app/models/user.rb
|
@@ -953,15 +1502,16 @@ create(:user) # creates the user and confirms it
|
|
953
1502
|
Modifying factories
|
954
1503
|
-------------------
|
955
1504
|
|
956
|
-
If you're given a set of factories (say, from a gem developer) but want to
|
957
|
-
|
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.
|
958
1508
|
|
959
1509
|
If a gem were to give you a User factory:
|
960
1510
|
|
961
1511
|
```ruby
|
962
1512
|
FactoryBot.define do
|
963
1513
|
factory :user do
|
964
|
-
full_name "John Doe"
|
1514
|
+
full_name { "John Doe" }
|
965
1515
|
sequence(:username) { |n| "user#{n}" }
|
966
1516
|
password { "password" }
|
967
1517
|
end
|
@@ -975,7 +1525,6 @@ FactoryBot.define do
|
|
975
1525
|
factory :application_user, parent: :user do
|
976
1526
|
full_name { "Jane Doe" }
|
977
1527
|
date_of_birth { 21.years.ago }
|
978
|
-
gender { "Female" }
|
979
1528
|
health { 90 }
|
980
1529
|
end
|
981
1530
|
end
|
@@ -988,7 +1537,6 @@ FactoryBot.modify do
|
|
988
1537
|
factory :user do
|
989
1538
|
full_name { "Jane Doe" }
|
990
1539
|
date_of_birth { 21.years.ago }
|
991
|
-
gender { "Female" }
|
992
1540
|
health { 90 }
|
993
1541
|
end
|
994
1542
|
end
|
@@ -1018,6 +1566,14 @@ To set the attributes for each of the factories, you can pass in a hash as you n
|
|
1018
1566
|
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
|
1019
1567
|
```
|
1020
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
|
+
|
1021
1577
|
`build_stubbed_list` will give you fully stubbed out instances:
|
1022
1578
|
|
1023
1579
|
```ruby
|
@@ -1040,7 +1596,7 @@ users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
|
|
1040
1596
|
Linting Factories
|
1041
1597
|
-----------------
|
1042
1598
|
|
1043
|
-
|
1599
|
+
factory\_bot allows for linting known factories:
|
1044
1600
|
|
1045
1601
|
```ruby
|
1046
1602
|
FactoryBot.lint
|
@@ -1067,9 +1623,10 @@ namespace :factory_bot do
|
|
1067
1623
|
desc "Verify that all FactoryBot factories are valid"
|
1068
1624
|
task lint: :environment do
|
1069
1625
|
if Rails.env.test?
|
1070
|
-
|
1071
|
-
|
1626
|
+
conn = ActiveRecord::Base.connection
|
1627
|
+
conn.transaction do
|
1072
1628
|
FactoryBot.lint
|
1629
|
+
raise ActiveRecord::Rollback
|
1073
1630
|
end
|
1074
1631
|
else
|
1075
1632
|
system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
|
@@ -1081,8 +1638,7 @@ end
|
|
1081
1638
|
|
1082
1639
|
After calling `FactoryBot.lint`, you'll likely want to clear out the
|
1083
1640
|
database, as records will most likely be created. The provided example above
|
1084
|
-
uses
|
1085
|
-
gem to your Gemfile under the appropriate groups.
|
1641
|
+
uses an sql transaction and rollback to leave the database clean.
|
1086
1642
|
|
1087
1643
|
You can lint factories selectively by passing only factories you want linted:
|
1088
1644
|
|
@@ -1126,7 +1682,7 @@ FactoryBot.lint verbose: true
|
|
1126
1682
|
Custom Construction
|
1127
1683
|
-------------------
|
1128
1684
|
|
1129
|
-
If you want to use
|
1685
|
+
If you want to use factory\_bot to construct an object where some attributes
|
1130
1686
|
are passed to `initialize` or if you want to do something other than simply
|
1131
1687
|
calling `new` on your build class, you can override the default behavior by
|
1132
1688
|
defining `initialize_with` on your factory. Example:
|
@@ -1154,7 +1710,7 @@ end
|
|
1154
1710
|
build(:user).name # Jane Doe
|
1155
1711
|
```
|
1156
1712
|
|
1157
|
-
Although
|
1713
|
+
Although factory\_bot is written to work with ActiveRecord out of the box, it
|
1158
1714
|
can also work with any Ruby class. For maximum compatibility with ActiveRecord,
|
1159
1715
|
the default initializer builds all instances by calling `new` on your build class
|
1160
1716
|
without any arguments. It then calls attribute writer methods to assign all the
|
@@ -1165,7 +1721,7 @@ You can override the initializer in order to:
|
|
1165
1721
|
|
1166
1722
|
* Build non-ActiveRecord objects that require arguments to `initialize`
|
1167
1723
|
* Use a method other than `new` to instantiate the instance
|
1168
|
-
* Do
|
1724
|
+
* Do wild things like decorate the instance after it's built
|
1169
1725
|
|
1170
1726
|
When using `initialize_with`, you don't have to declare the class itself when
|
1171
1727
|
calling `new`; however, any other class methods you want to call will have to
|
@@ -1192,7 +1748,7 @@ factory :user do
|
|
1192
1748
|
|
1193
1749
|
name "John Doe"
|
1194
1750
|
|
1195
|
-
initialize_with { new(attributes) }
|
1751
|
+
initialize_with { new(**attributes) }
|
1196
1752
|
end
|
1197
1753
|
```
|
1198
1754
|
|
@@ -1227,7 +1783,7 @@ build(:user)
|
|
1227
1783
|
User.new('value')
|
1228
1784
|
```
|
1229
1785
|
|
1230
|
-
This prevents duplicate assignment; in versions of
|
1786
|
+
This prevents duplicate assignment; in versions of factory\_bot before 4.0, it
|
1231
1787
|
would run this:
|
1232
1788
|
|
1233
1789
|
```ruby
|
@@ -1413,12 +1969,12 @@ with associations, as below:
|
|
1413
1969
|
|
1414
1970
|
```ruby
|
1415
1971
|
FactoryBot.define do
|
1416
|
-
factory :united_states, class: Location do
|
1972
|
+
factory :united_states, class: "Location" do
|
1417
1973
|
name { 'United States' }
|
1418
1974
|
association :location_group, factory: :north_america
|
1419
1975
|
end
|
1420
1976
|
|
1421
|
-
factory :north_america, class: LocationGroup do
|
1977
|
+
factory :north_america, class: "LocationGroup" do
|
1422
1978
|
name { 'North America' }
|
1423
1979
|
end
|
1424
1980
|
end
|