factory_bot 4.10.0 → 6.0.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/.yardopts +1 -0
- data/CONTRIBUTING.md +52 -13
- data/GETTING_STARTED.md +613 -182
- data/LICENSE +1 -1
- data/NEWS.md +358 -0
- data/README.md +30 -26
- data/lib/factory_bot.rb +71 -140
- data/lib/factory_bot/aliases.rb +2 -2
- data/lib/factory_bot/attribute.rb +4 -39
- data/lib/factory_bot/attribute/association.rb +2 -2
- data/lib/factory_bot/attribute/dynamic.rb +2 -1
- data/lib/factory_bot/attribute_assigner.rb +24 -10
- data/lib/factory_bot/attribute_list.rb +3 -2
- data/lib/factory_bot/callback.rb +3 -10
- data/lib/factory_bot/configuration.rb +15 -19
- data/lib/factory_bot/declaration.rb +5 -5
- data/lib/factory_bot/declaration/association.rb +14 -1
- data/lib/factory_bot/declaration/dynamic.rb +3 -1
- data/lib/factory_bot/declaration/implicit.rb +7 -2
- data/lib/factory_bot/declaration_list.rb +3 -3
- data/lib/factory_bot/decorator.rb +5 -5
- data/lib/factory_bot/decorator/attribute_hash.rb +1 -1
- data/lib/factory_bot/decorator/invocation_tracker.rb +1 -1
- data/lib/factory_bot/definition.rb +49 -20
- data/lib/factory_bot/definition_hierarchy.rb +1 -11
- data/lib/factory_bot/definition_proxy.rb +125 -43
- 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 +9 -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 -37
- 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.rb +2 -2
- data/lib/factory_bot/syntax/default.rb +12 -24
- data/lib/factory_bot/syntax/methods.rb +32 -9
- data/lib/factory_bot/trait.rb +7 -4
- data/lib/factory_bot/version.rb +1 -1
- metadata +50 -65
- data/.autotest +0 -9
- data/.gitignore +0 -10
- data/.rspec +0 -3
- data/.simplecov +0 -4
- data/.travis.yml +0 -58
- data/Appraisals +0 -23
- data/Gemfile +0 -8
- data/Gemfile.lock +0 -103
- data/NEWS +0 -298
- data/Rakefile +0 -36
- data/cucumber.yml +0 -1
- data/factory_bot.gemspec +0 -36
- data/factory_girl.gemspec +0 -40
- data/gemfiles/3.2.gemfile +0 -10
- data/gemfiles/3.2.gemfile.lock +0 -107
- data/gemfiles/4.0.gemfile +0 -10
- data/gemfiles/4.0.gemfile.lock +0 -107
- data/gemfiles/4.1.gemfile +0 -10
- data/gemfiles/4.1.gemfile.lock +0 -106
- data/gemfiles/4.2.gemfile +0 -10
- data/gemfiles/4.2.gemfile.lock +0 -106
- data/gemfiles/5.0.gemfile +0 -10
- data/gemfiles/5.0.gemfile.lock +0 -104
- data/gemfiles/5.1.gemfile +0 -10
- data/gemfiles/5.1.gemfile.lock +0 -104
- data/lib/factory_bot/attribute/static.rb +0 -16
- data/lib/factory_bot/declaration/static.rb +0 -26
- data/lib/factory_bot/decorator/class_key_hash.rb +0 -28
- data/lib/factory_girl.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f060c0b183a1d65b69542b917a19ad16d1de52d7f69dd692839eceaa316ad2a
|
4
|
+
data.tar.gz: 4f2286b4cabd1b562352fed5d20f00ba29a7ede89c8a855657f0f0075527d0f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67230581c94a6e476ba8fa8aeb123d2ccfe18cd14af2d9d292cc871d9d1dda63cbd827e455c165f9eac7211fe9770fd41b87ab349db55a000e32ee23a319fda1
|
7
|
+
data.tar.gz: 8ce7be2d33e08ddc24c50082a2025c7c59877457558720dfbdc4920e70f881e30a52cfb322fe129ae9690b512bbf42059bb24db973c81b3a585d8693ff484a1c
|
data/.yardopts
CHANGED
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,63 @@ 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
|
+
## Running the test suite
|
55
|
+
|
56
|
+
The default rake task will run the full test suite and [standard]:
|
57
|
+
|
58
|
+
```sh
|
59
|
+
bundle exec rake
|
60
|
+
```
|
61
|
+
|
62
|
+
You can also run a single group of tests (unit, spec, or feature)
|
63
|
+
|
64
|
+
```sh
|
65
|
+
bundle exec rake spec:unit
|
66
|
+
bundle exec rake spec:acceptance
|
67
|
+
bundle exec rake features
|
68
|
+
```
|
69
|
+
|
70
|
+
To run an individual rspec test, you can provide a path and line number:
|
71
|
+
|
72
|
+
```sh
|
73
|
+
bundle exec rspec spec/path/to/spec.rb:123
|
74
|
+
```
|
75
|
+
|
76
|
+
You can run tests with a specific version of rails via [appraisal]. To run
|
77
|
+
the default rake task against Rails 6, for example:
|
78
|
+
|
79
|
+
```sh
|
80
|
+
bundle exec appraisal 6.0 rake
|
81
|
+
```
|
82
|
+
|
83
|
+
## Formatting
|
84
|
+
|
85
|
+
Use [standard] to automatically format your code:
|
86
|
+
|
87
|
+
```sh
|
88
|
+
bundle exec rake standard:fix
|
89
|
+
```
|
90
|
+
|
55
91
|
[repo]: https://github.com/thoughtbot/factory_bot/tree/master
|
56
92
|
[fork]: https://help.github.com/articles/fork-a-repo/
|
57
93
|
[branch]: https://help.github.com/articles/creating-and-deleting-branches-within-your-repository/
|
58
94
|
[pr]: https://help.github.com/articles/using-pull-requests/
|
95
|
+
[standard]: https://github.com/testdouble/standard
|
96
|
+
[appraisal]: https://github.com/thoughtbot/appraisal
|
97
|
+
[reproduction script]: https://github.com/thoughtbot/factory_bot/blob/master/.github/REPRODUCTION_SCRIPT.rb
|
59
98
|
|
60
99
|
Inspired by https://github.com/middleman/middleman-heroku/blob/master/CONTRIBUTING.md
|
data/GETTING_STARTED.md
CHANGED
@@ -1,42 +1,117 @@
|
|
1
1
|
Getting Started
|
2
2
|
===============
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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
|
+
+ [Specifying the factory](#specifying-the-factory)
|
41
|
+
+ [Overriding attributes](#overriding-attributes)
|
42
|
+
+ [Build strategies](#build-strategies-1)
|
43
|
+
+ [`has_many` associations](#has_many-associations)
|
44
|
+
+ [`has_and_belongs_to_many` associations](#has_and_belongs_to_many-associations)
|
45
|
+
+ [Polymorphic associations](#polymorphic-associations)
|
46
|
+
* [Sequences](#sequences)
|
47
|
+
+ [Global sequences](#global-sequences)
|
48
|
+
+ [With dynamic attributes](#with-dynamic-attributes)
|
49
|
+
+ [As implicit attributes](#as-implicit-attributes)
|
50
|
+
+ [Inline sequences](#inline-sequences)
|
51
|
+
+ [Initial value](#initial-value)
|
52
|
+
+ [Without a block](#without-a-block)
|
53
|
+
+ [Aliases](#aliases-1)
|
54
|
+
+ [Rewinding](#rewinding)
|
55
|
+
* [Traits](#traits)
|
56
|
+
+ [Defining traits](#defining-traits)
|
57
|
+
+ [As implicit attributes](#as-implicit-attributes-1)
|
58
|
+
+ [Attribute precedence](#attribute-precedence)
|
59
|
+
+ [In child factories](#in-child-factories)
|
60
|
+
+ [Using traits](#using-traits)
|
61
|
+
+ [With associations](#with-associations-1)
|
62
|
+
+ [Traits within traits](#traits-within-traits)
|
63
|
+
+ [With transient attributes](#with-transient-attributes)
|
64
|
+
+ [Enum traits](#enum-traits)
|
65
|
+
* [Callbacks](#callbacks)
|
66
|
+
+ [Default callbacks](#default-callbacks)
|
67
|
+
+ [Multiple callbacks](#multiple-callbacks)
|
68
|
+
+ [Global callbacks](#global-callbacks)
|
69
|
+
+ [Symbol#to_proc](#symbolto_proc)
|
70
|
+
* [Modifying factories](#modifying-factories)
|
71
|
+
* [Building or Creating Multiple Records](#building-or-creating-multiple-records)
|
72
|
+
* [Linting Factories](#linting-factories)
|
73
|
+
* [Custom Construction](#custom-construction)
|
74
|
+
* [Custom Strategies](#custom-strategies)
|
75
|
+
* [Custom Callbacks](#custom-callbacks)
|
76
|
+
* [Custom Methods to Persist Objects](#custom-methods-to-persist-objects)
|
77
|
+
* [ActiveSupport Instrumentation](#activesupport-instrumentation)
|
78
|
+
* [Rails Preloaders and RSpec](#rails-preloaders-and-rspec)
|
79
|
+
* [Using Without Bundler](#using-without-bundler)
|
80
|
+
|
81
|
+
Setup
|
82
|
+
-----
|
83
|
+
|
84
|
+
### Update Your Gemfile
|
85
|
+
|
86
|
+
If you're using Rails:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
gem "factory_bot_rails"
|
90
|
+
```
|
91
|
+
|
92
|
+
If you're *not* using Rails:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
gem "factory_bot"
|
96
|
+
```
|
97
|
+
|
98
|
+
### Configure your test suite
|
99
|
+
|
100
|
+
#### RSpec
|
101
|
+
|
102
|
+
If you're using Rails, add the following configuration to
|
103
|
+
`spec/support/factory_bot.rb` and be sure to require that file in
|
104
|
+
`rails_helper.rb`:
|
32
105
|
|
33
106
|
```ruby
|
34
|
-
# spec/support/factory_bot.rb
|
35
107
|
RSpec.configure do |config|
|
36
108
|
config.include FactoryBot::Syntax::Methods
|
37
109
|
end
|
110
|
+
```
|
38
111
|
|
39
|
-
|
112
|
+
If you're *not* using Rails:
|
113
|
+
|
114
|
+
```ruby
|
40
115
|
RSpec.configure do |config|
|
41
116
|
config.include FactoryBot::Syntax::Methods
|
42
117
|
|
@@ -46,13 +121,7 @@ RSpec.configure do |config|
|
|
46
121
|
end
|
47
122
|
```
|
48
123
|
|
49
|
-
|
50
|
-
|
51
|
-
```ruby
|
52
|
-
require 'support/factory_bot'
|
53
|
-
```
|
54
|
-
|
55
|
-
### Test::Unit
|
124
|
+
#### Test::Unit
|
56
125
|
|
57
126
|
```ruby
|
58
127
|
class Test::Unit::TestCase
|
@@ -60,14 +129,14 @@ class Test::Unit::TestCase
|
|
60
129
|
end
|
61
130
|
```
|
62
131
|
|
63
|
-
|
132
|
+
#### Cucumber
|
64
133
|
|
65
134
|
```ruby
|
66
135
|
# env.rb (Rails example location - RAILS_ROOT/features/support/env.rb)
|
67
136
|
World(FactoryBot::Syntax::Methods)
|
68
137
|
```
|
69
138
|
|
70
|
-
|
139
|
+
#### Spinach
|
71
140
|
|
72
141
|
```ruby
|
73
142
|
class Spinach::FeatureSteps
|
@@ -75,7 +144,7 @@ class Spinach::FeatureSteps
|
|
75
144
|
end
|
76
145
|
```
|
77
146
|
|
78
|
-
|
147
|
+
#### Minitest
|
79
148
|
|
80
149
|
```ruby
|
81
150
|
class Minitest::Unit::TestCase
|
@@ -83,7 +152,7 @@ class Minitest::Unit::TestCase
|
|
83
152
|
end
|
84
153
|
```
|
85
154
|
|
86
|
-
|
155
|
+
#### Minitest::Spec
|
87
156
|
|
88
157
|
```ruby
|
89
158
|
class Minitest::Spec
|
@@ -91,7 +160,7 @@ class Minitest::Spec
|
|
91
160
|
end
|
92
161
|
```
|
93
162
|
|
94
|
-
|
163
|
+
#### minitest-rails
|
95
164
|
|
96
165
|
```ruby
|
97
166
|
class ActiveSupport::TestCase
|
@@ -99,35 +168,71 @@ class ActiveSupport::TestCase
|
|
99
168
|
end
|
100
169
|
```
|
101
170
|
|
102
|
-
If you do not include `FactoryBot::Syntax::Methods` in your test suite, then all
|
171
|
+
If you do not include `FactoryBot::Syntax::Methods` in your test suite, then all
|
172
|
+
factory\_bot methods will need to be prefaced with `FactoryBot`.
|
103
173
|
|
104
174
|
Defining factories
|
105
175
|
------------------
|
106
176
|
|
107
|
-
|
177
|
+
### Factory name and attributes
|
178
|
+
|
179
|
+
Each factory has a name and a set of attributes. The name is used to guess the
|
180
|
+
class of the object by default:
|
108
181
|
|
109
182
|
```ruby
|
110
183
|
# This will guess the User class
|
111
184
|
FactoryBot.define do
|
112
185
|
factory :user do
|
113
|
-
first_name "John"
|
114
|
-
last_name "Doe"
|
115
|
-
admin false
|
186
|
+
first_name { "John" }
|
187
|
+
last_name { "Doe" }
|
188
|
+
admin { false }
|
116
189
|
end
|
190
|
+
end
|
191
|
+
```
|
117
192
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
193
|
+
### Specifying the class explicitly
|
194
|
+
|
195
|
+
It is also possible to explicitly specify the class:
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
# This will use the User class (otherwise Admin would have been guessed)
|
199
|
+
factory :admin, class: User
|
200
|
+
```
|
201
|
+
|
202
|
+
If the constant is not available
|
203
|
+
(if you are using a Rails engine that waits to load models, for example),
|
204
|
+
you can also pass a symbol or string,
|
205
|
+
which factory\_bot will constantize later, once you start building objects:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
# It's OK if Doorkeeper::AccessToken isn't loaded yet
|
209
|
+
factory :access_token, class: "Doorkeeper::AccessToken"
|
210
|
+
```
|
211
|
+
|
212
|
+
### Hash attributes
|
213
|
+
|
214
|
+
Because of the block syntax in Ruby, defining attributes as `Hash`es (for
|
215
|
+
serialized/JSON columns, for example) requires two sets of curly brackets:
|
216
|
+
|
217
|
+
```ruby
|
218
|
+
factory :program do
|
219
|
+
configuration { { auto_resolve: false, auto_define: true } }
|
124
220
|
end
|
125
221
|
```
|
126
222
|
|
127
|
-
|
223
|
+
### Best practices
|
224
|
+
|
225
|
+
It is recommended that you have one factory for each class that provides
|
226
|
+
the simplest set of attributes necessary to create an instance of that class. If
|
227
|
+
you're creating ActiveRecord objects, that means that you should only provide
|
228
|
+
attributes that are required through validations and that do not have defaults.
|
229
|
+
Other factories can be created through inheritance to cover common scenarios for
|
230
|
+
each class.
|
128
231
|
|
129
232
|
Attempting to define multiple factories with the same name will raise an error.
|
130
233
|
|
234
|
+
### Definition file paths
|
235
|
+
|
131
236
|
Factories can be defined anywhere, but will be automatically loaded after
|
132
237
|
calling `FactoryBot.find_definitions` if factories are defined in files at the
|
133
238
|
following locations:
|
@@ -137,10 +242,20 @@ following locations:
|
|
137
242
|
test/factories/*.rb
|
138
243
|
spec/factories/*.rb
|
139
244
|
|
245
|
+
### Static Attributes
|
246
|
+
|
247
|
+
Static attributes (without a block) are no longer available in factory\_bot 5.
|
248
|
+
You can read more about the decision to remove them in
|
249
|
+
[this blog post](https://robots.thoughtbot.com/deprecating-static-attributes-in-factory_bot-4-11).
|
250
|
+
|
251
|
+
|
140
252
|
Using factories
|
141
253
|
---------------
|
142
254
|
|
143
|
-
|
255
|
+
### Build strategies
|
256
|
+
|
257
|
+
factory\_bot supports several different build strategies: build, create,
|
258
|
+
attributes\_for and build\_stubbed:
|
144
259
|
|
145
260
|
```ruby
|
146
261
|
# Returns a User instance that's not saved
|
@@ -161,7 +276,10 @@ create(:user) do |user|
|
|
161
276
|
end
|
162
277
|
```
|
163
278
|
|
164
|
-
|
279
|
+
### Attribute overrides
|
280
|
+
|
281
|
+
No matter which strategy is used, it's possible to override the defined
|
282
|
+
attributes by passing a hash:
|
165
283
|
|
166
284
|
```ruby
|
167
285
|
# Build a User instance and override the first_name property
|
@@ -170,40 +288,25 @@ user.first_name
|
|
170
288
|
# => "Joe"
|
171
289
|
```
|
172
290
|
|
173
|
-
|
174
|
-
------------------
|
175
|
-
|
176
|
-
Most factory attributes can be added using static values that are evaluated when
|
177
|
-
the factory is defined, but some attributes (such as associations and other
|
178
|
-
attributes that must be dynamically generated) will need values assigned each
|
179
|
-
time an instance is generated. These "dynamic" attributes can be added by passing a
|
180
|
-
block instead of a parameter:
|
181
|
-
|
182
|
-
```ruby
|
183
|
-
factory :user do
|
184
|
-
# ...
|
185
|
-
activation_code { User.generate_activation_code }
|
186
|
-
date_of_birth { 21.years.ago }
|
187
|
-
end
|
188
|
-
```
|
291
|
+
### `build_stubbed` and `Marshal.dump`
|
189
292
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
```ruby
|
194
|
-
factory :program do
|
195
|
-
configuration { { auto_resolve: false, auto_define: true } }
|
196
|
-
end
|
197
|
-
```
|
293
|
+
Note that objects created with `build_stubbed` cannot be serialized with
|
294
|
+
`Marshal.dump`, since factory\_bot defines singleton methods on these objects.
|
198
295
|
|
199
296
|
Aliases
|
200
297
|
-------
|
201
|
-
|
298
|
+
|
299
|
+
factory\_bot allows you to define aliases to existing factories to make them
|
300
|
+
easier to re-use. This could come in handy when, for example, your Post object
|
301
|
+
has an author attribute that actually refers to an instance of a User class.
|
302
|
+
While normally factory\_bot can infer the factory name from the association name,
|
303
|
+
in this case it will look for an author factory in vain. So, alias your user
|
304
|
+
factory so it can be used under alias names.
|
202
305
|
|
203
306
|
```ruby
|
204
307
|
factory :user, aliases: [:author, :commenter] do
|
205
|
-
first_name
|
206
|
-
last_name
|
308
|
+
first_name { "John" }
|
309
|
+
last_name { "Doe" }
|
207
310
|
date_of_birth { 18.years.ago }
|
208
311
|
end
|
209
312
|
|
@@ -211,15 +314,15 @@ factory :post do
|
|
211
314
|
author
|
212
315
|
# instead of
|
213
316
|
# association :author, factory: :user
|
214
|
-
title "How to read a book effectively"
|
215
|
-
body
|
317
|
+
title { "How to read a book effectively" }
|
318
|
+
body { "There are five steps involved." }
|
216
319
|
end
|
217
320
|
|
218
321
|
factory :comment do
|
219
322
|
commenter
|
220
323
|
# instead of
|
221
324
|
# association :commenter, factory: :user
|
222
|
-
body "Great article!"
|
325
|
+
body { "Great article!" }
|
223
326
|
end
|
224
327
|
```
|
225
328
|
|
@@ -231,8 +334,8 @@ that is yielded to dynamic attribute blocks:
|
|
231
334
|
|
232
335
|
```ruby
|
233
336
|
factory :user do
|
234
|
-
first_name "Joe"
|
235
|
-
last_name "Blow"
|
337
|
+
first_name { "Joe" }
|
338
|
+
last_name { "Blow" }
|
236
339
|
email { "#{first_name}.#{last_name}@example.com".downcase }
|
237
340
|
end
|
238
341
|
|
@@ -243,40 +346,82 @@ create(:user, last_name: "Doe").email
|
|
243
346
|
Transient Attributes
|
244
347
|
--------------------
|
245
348
|
|
246
|
-
|
349
|
+
### With other attributes
|
350
|
+
|
351
|
+
There may be times where your code can be DRYed up by passing in transient
|
352
|
+
attributes to factories. You can access transient attributes within other
|
353
|
+
attributes (see [Dependent Attributes](#dependent-attributes)):
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
factory :user do
|
357
|
+
transient do
|
358
|
+
rockstar { true }
|
359
|
+
end
|
360
|
+
|
361
|
+
name { "John Doe#{" - Rockstar" if rockstar}" }
|
362
|
+
end
|
363
|
+
|
364
|
+
create(:user).name
|
365
|
+
#=> "John Doe - ROCKSTAR"
|
366
|
+
|
367
|
+
create(:user, rockstar: false).name
|
368
|
+
#=> "John Doe"
|
369
|
+
```
|
370
|
+
|
371
|
+
### With `attributes_for`
|
372
|
+
|
373
|
+
Transient attributes will be ignored within attributes\_for and won't be set on
|
374
|
+
the model, even if the attribute exists or you attempt to override it.
|
375
|
+
|
376
|
+
### With callbacks
|
377
|
+
|
378
|
+
If you need to access the evaluator in a factory\_bot callback,
|
379
|
+
you'll need to declare a second block argument (for the evaluator) and access
|
380
|
+
transient attributes from there.
|
247
381
|
|
248
382
|
```ruby
|
249
383
|
factory :user do
|
250
384
|
transient do
|
251
|
-
|
252
|
-
upcased false
|
385
|
+
upcased { false }
|
253
386
|
end
|
254
387
|
|
255
|
-
name
|
256
|
-
email { "#{name.downcase}@example.com" }
|
388
|
+
name { "John Doe" }
|
257
389
|
|
258
390
|
after(:create) do |user, evaluator|
|
259
391
|
user.name.upcase! if evaluator.upcased
|
260
392
|
end
|
261
393
|
end
|
262
394
|
|
395
|
+
create(:user).name
|
396
|
+
#=> "John Doe"
|
397
|
+
|
263
398
|
create(:user, upcased: true).name
|
264
|
-
#=> "JOHN DOE
|
399
|
+
#=> "JOHN DOE"
|
265
400
|
```
|
266
401
|
|
267
|
-
|
268
|
-
attributes will be ignored within attributes\_for and won't be set on the model,
|
269
|
-
even if the attribute exists or you attempt to override it.
|
402
|
+
### With associations
|
270
403
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
404
|
+
Transient [associations](#associations) are not supported in factory\_bot.
|
405
|
+
Associations within the transient block will be treated as regular,
|
406
|
+
non-transient associations.
|
407
|
+
|
408
|
+
If needed, you can generally work around this by building a factory within a
|
409
|
+
transient attribute:
|
410
|
+
|
411
|
+
```ruby
|
412
|
+
factory :post
|
413
|
+
|
414
|
+
factory :user do
|
415
|
+
transient do
|
416
|
+
post { build(:post) }
|
417
|
+
end
|
418
|
+
end
|
419
|
+
```
|
275
420
|
|
276
421
|
Method Name / Reserved Word Attributes
|
277
422
|
-------------------------------
|
278
423
|
|
279
|
-
If your attributes conflict with existing methods or reserved words you can define them with `add_attribute`.
|
424
|
+
If your attributes conflict with existing methods or reserved words (all methods in the [DefinitionProxy](https://github.com/thoughtbot/factory_bot/blob/master/lib/factory_bot/definition_proxy.rb) class) you can define them with `add_attribute`.
|
280
425
|
|
281
426
|
```ruby
|
282
427
|
factory :dna do
|
@@ -292,14 +437,17 @@ end
|
|
292
437
|
Inheritance
|
293
438
|
-----------
|
294
439
|
|
295
|
-
|
440
|
+
### Nested factories
|
441
|
+
|
442
|
+
You can easily create multiple factories for the same class without repeating
|
443
|
+
common attributes by nesting factories:
|
296
444
|
|
297
445
|
```ruby
|
298
446
|
factory :post do
|
299
|
-
title "A title"
|
447
|
+
title { "A title" }
|
300
448
|
|
301
449
|
factory :approved_post do
|
302
|
-
approved true
|
450
|
+
approved { true }
|
303
451
|
end
|
304
452
|
end
|
305
453
|
|
@@ -308,18 +456,22 @@ approved_post.title # => "A title"
|
|
308
456
|
approved_post.approved # => true
|
309
457
|
```
|
310
458
|
|
459
|
+
### Assigning parent explicitly
|
460
|
+
|
311
461
|
You can also assign the parent explicitly:
|
312
462
|
|
313
463
|
```ruby
|
314
464
|
factory :post do
|
315
|
-
title "A title"
|
465
|
+
title { "A title" }
|
316
466
|
end
|
317
467
|
|
318
468
|
factory :approved_post, parent: :post do
|
319
|
-
approved true
|
469
|
+
approved { true }
|
320
470
|
end
|
321
471
|
```
|
322
472
|
|
473
|
+
### Best practices
|
474
|
+
|
323
475
|
As mentioned above, it's good practice to define a basic factory for each class
|
324
476
|
with only the attributes required to create it. Then, create more specific
|
325
477
|
factories that inherit from this basic parent. Factory definitions are still
|
@@ -328,7 +480,10 @@ code, so keep them DRY.
|
|
328
480
|
Associations
|
329
481
|
------------
|
330
482
|
|
331
|
-
|
483
|
+
### Implicit definition
|
484
|
+
|
485
|
+
It's possible to set up associations within factories. If the factory name is
|
486
|
+
the same as the association name, the factory name can be left out.
|
332
487
|
|
333
488
|
```ruby
|
334
489
|
factory :post do
|
@@ -337,18 +492,95 @@ factory :post do
|
|
337
492
|
end
|
338
493
|
```
|
339
494
|
|
340
|
-
|
495
|
+
### Explicit definition
|
496
|
+
|
497
|
+
You can define associations explicitly. This can be handy especially when
|
498
|
+
[Overriding attributes](#overriding-attributes)
|
499
|
+
|
500
|
+
```ruby
|
501
|
+
factory :post do
|
502
|
+
# ...
|
503
|
+
association :author
|
504
|
+
end
|
505
|
+
```
|
506
|
+
|
507
|
+
### Specifying the factory
|
508
|
+
|
509
|
+
You can specify a different factory (although [Aliases](#aliases) might also
|
510
|
+
help you out here).
|
511
|
+
|
512
|
+
Implicitly:
|
513
|
+
|
514
|
+
```ruby
|
515
|
+
factory :post do
|
516
|
+
# ...
|
517
|
+
author factory: :user
|
518
|
+
end
|
519
|
+
```
|
520
|
+
|
521
|
+
Explicitly:
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
factory :post do
|
525
|
+
# ...
|
526
|
+
association :author, factory: :user
|
527
|
+
end
|
528
|
+
```
|
529
|
+
|
530
|
+
### Overriding attributes
|
531
|
+
|
532
|
+
You can also override attributes.
|
533
|
+
|
534
|
+
Implicitly:
|
535
|
+
|
536
|
+
```ruby
|
537
|
+
factory :post do
|
538
|
+
# ...
|
539
|
+
author factory: :author, last_name: "Writely"
|
540
|
+
end
|
541
|
+
```
|
542
|
+
|
543
|
+
Explicitly:
|
544
|
+
|
341
545
|
|
342
546
|
```ruby
|
343
547
|
factory :post do
|
344
548
|
# ...
|
345
|
-
association :author,
|
549
|
+
association :author, last_name: "Writely"
|
550
|
+
end
|
551
|
+
```
|
552
|
+
|
553
|
+
### Build strategies
|
554
|
+
|
555
|
+
In factory\_bot 5, associations default to using the same build strategy as
|
556
|
+
their parent object:
|
557
|
+
|
558
|
+
```ruby
|
559
|
+
FactoryBot.define do
|
560
|
+
factory :author
|
561
|
+
|
562
|
+
factory :post do
|
563
|
+
author
|
564
|
+
end
|
346
565
|
end
|
566
|
+
|
567
|
+
post = build(:post)
|
568
|
+
post.new_record? # => true
|
569
|
+
post.author.new_record? # => true
|
570
|
+
|
571
|
+
post = create(:post)
|
572
|
+
post.new_record? # => false
|
573
|
+
post.author.new_record? # => false
|
347
574
|
```
|
348
575
|
|
349
|
-
|
576
|
+
This is different than the default behavior for previous versions of
|
577
|
+
factory\_bot, where the association strategy would not always match the strategy
|
578
|
+
of the parent object. If you want to continue using the old behavior, you can
|
579
|
+
set the `use_parent_strategy` configuration option to `false`.
|
350
580
|
|
351
581
|
```ruby
|
582
|
+
FactoryBot.use_parent_strategy = false
|
583
|
+
|
352
584
|
# Builds and saves a User and a Post
|
353
585
|
post = create(:post)
|
354
586
|
post.new_record? # => false
|
@@ -360,9 +592,11 @@ post.new_record? # => true
|
|
360
592
|
post.author.new_record? # => false
|
361
593
|
```
|
362
594
|
|
363
|
-
To not save the associated object, specify strategy: :build in the factory:
|
595
|
+
To not save the associated object, specify `strategy: :build` in the factory:
|
364
596
|
|
365
597
|
```ruby
|
598
|
+
FactoryBot.use_parent_strategy = false
|
599
|
+
|
366
600
|
factory :post do
|
367
601
|
# ...
|
368
602
|
association :author, factory: :user, strategy: :build
|
@@ -383,6 +617,8 @@ factory :post do
|
|
383
617
|
author strategy: :build # <<< this does *not* work; causes author_id to be nil
|
384
618
|
```
|
385
619
|
|
620
|
+
### `has_many` associations
|
621
|
+
|
386
622
|
Generating data for a `has_many` relationship is a bit more involved,
|
387
623
|
depending on the amount of flexibility desired, but here's a surefire example
|
388
624
|
of generating associated data.
|
@@ -392,20 +628,20 @@ FactoryBot.define do
|
|
392
628
|
|
393
629
|
# post factory with a `belongs_to` association for the user
|
394
630
|
factory :post do
|
395
|
-
title "Through the Looking Glass"
|
631
|
+
title { "Through the Looking Glass" }
|
396
632
|
user
|
397
633
|
end
|
398
634
|
|
399
635
|
# user factory without associated posts
|
400
636
|
factory :user do
|
401
|
-
name "John Doe"
|
637
|
+
name { "John Doe" }
|
402
638
|
|
403
639
|
# user_with_posts will create post data after the user has been created
|
404
640
|
factory :user_with_posts do
|
405
641
|
# posts_count is declared as a transient attribute and available in
|
406
642
|
# attributes on the factory, as well as the callback via the evaluator
|
407
643
|
transient do
|
408
|
-
posts_count 5
|
644
|
+
posts_count { 5 }
|
409
645
|
end
|
410
646
|
|
411
647
|
# the after(:create) yields two values; the user instance itself and the
|
@@ -428,6 +664,8 @@ create(:user_with_posts).posts.length # 5
|
|
428
664
|
create(:user_with_posts, posts_count: 15).posts.length # 15
|
429
665
|
```
|
430
666
|
|
667
|
+
### `has_and_belongs_to_many` associations
|
668
|
+
|
431
669
|
Generating data for a `has_and_belongs_to_many` relationship is very similar
|
432
670
|
to the above `has_many` relationship, with a small change, you need to pass an
|
433
671
|
array of objects to the model's pluralized attribute name rather than a single
|
@@ -441,13 +679,13 @@ FactoryBot.define do
|
|
441
679
|
|
442
680
|
# language factory with a `belongs_to` association for the profile
|
443
681
|
factory :language do
|
444
|
-
title "Through the Looking Glass"
|
682
|
+
title { "Through the Looking Glass" }
|
445
683
|
profile
|
446
684
|
end
|
447
685
|
|
448
686
|
# profile factory without associated languages
|
449
687
|
factory :profile do
|
450
|
-
name "John Doe"
|
688
|
+
name { "John Doe" }
|
451
689
|
|
452
690
|
# profile_with_languages will create language data after the profile has
|
453
691
|
# been created
|
@@ -455,7 +693,7 @@ FactoryBot.define do
|
|
455
693
|
# languages_count is declared as an ignored attribute and available in
|
456
694
|
# attributes on the factory, as well as the callback via the evaluator
|
457
695
|
transient do
|
458
|
-
languages_count 5
|
696
|
+
languages_count { 5 }
|
459
697
|
end
|
460
698
|
|
461
699
|
# the after(:create) yields two values; the profile instance itself and
|
@@ -479,9 +717,43 @@ create(:profile_with_languages).languages.length # 5
|
|
479
717
|
create(:profile_with_languages, languages_count: 15).languages.length # 15
|
480
718
|
```
|
481
719
|
|
720
|
+
### Polymorphic associations
|
721
|
+
|
722
|
+
Polymorphic associations can be handled with traits:
|
723
|
+
|
724
|
+
```ruby
|
725
|
+
FactoryBot.define do
|
726
|
+
factory :video
|
727
|
+
factory :photo
|
728
|
+
|
729
|
+
factory :comment do
|
730
|
+
for_photo # default to the :for_photo trait if none is specified
|
731
|
+
|
732
|
+
trait :for_video do
|
733
|
+
association :commentable, factory: :video
|
734
|
+
end
|
735
|
+
|
736
|
+
trait :for_photo do
|
737
|
+
association :commentable, factory: :photo
|
738
|
+
end
|
739
|
+
end
|
740
|
+
end
|
741
|
+
```
|
742
|
+
|
743
|
+
This allows us to do:
|
744
|
+
|
745
|
+
```ruby
|
746
|
+
create(:comment)
|
747
|
+
create(:comment, :for_video)
|
748
|
+
create(:comment, :for_photo)
|
749
|
+
```
|
750
|
+
|
751
|
+
|
482
752
|
Sequences
|
483
753
|
---------
|
484
754
|
|
755
|
+
### Global sequences
|
756
|
+
|
485
757
|
Unique values in a specific format (for example, e-mail addresses) can be
|
486
758
|
generated using sequences. Sequences are defined by calling `sequence` in a
|
487
759
|
definition block, and values in a sequence are generated by calling
|
@@ -502,6 +774,8 @@ generate :email
|
|
502
774
|
# => "person2@example.com"
|
503
775
|
```
|
504
776
|
|
777
|
+
### With dynamic attributes
|
778
|
+
|
505
779
|
Sequences can be used in dynamic attributes:
|
506
780
|
|
507
781
|
```ruby
|
@@ -510,6 +784,8 @@ factory :invite do
|
|
510
784
|
end
|
511
785
|
```
|
512
786
|
|
787
|
+
### As implicit attributes
|
788
|
+
|
513
789
|
Or as implicit attributes:
|
514
790
|
|
515
791
|
```ruby
|
@@ -518,6 +794,11 @@ factory :user do
|
|
518
794
|
end
|
519
795
|
```
|
520
796
|
|
797
|
+
Note that defining sequences as implicit attributes will not work if you have a
|
798
|
+
factory with the same name as the sequence.
|
799
|
+
|
800
|
+
### Inline sequences
|
801
|
+
|
521
802
|
And it's also possible to define an in-line sequence that is only used in
|
522
803
|
a particular factory:
|
523
804
|
|
@@ -527,7 +808,10 @@ factory :user do
|
|
527
808
|
end
|
528
809
|
```
|
529
810
|
|
530
|
-
|
811
|
+
### Initial value
|
812
|
+
|
813
|
+
You can override the initial value. Any value that response to the `#next`
|
814
|
+
method will work (e.g. 1, 2, 3, 'a', 'b', 'c')
|
531
815
|
|
532
816
|
```ruby
|
533
817
|
factory :user do
|
@@ -535,6 +819,8 @@ factory :user do
|
|
535
819
|
end
|
536
820
|
```
|
537
821
|
|
822
|
+
### Without a block
|
823
|
+
|
538
824
|
Without a block, the value will increment itself, starting at its initial value:
|
539
825
|
|
540
826
|
```ruby
|
@@ -543,6 +829,8 @@ factory :post do
|
|
543
829
|
end
|
544
830
|
```
|
545
831
|
|
832
|
+
### Aliases
|
833
|
+
|
546
834
|
Sequences can also have aliases. The sequence aliases share the same counter:
|
547
835
|
|
548
836
|
```ruby
|
@@ -572,6 +860,8 @@ end
|
|
572
860
|
|
573
861
|
The value just needs to support the `#next` method. Here the next value will be 'a', then 'b', etc.
|
574
862
|
|
863
|
+
### Rewinding
|
864
|
+
|
575
865
|
Sequences can also be rewound with `FactoryBot.rewind_sequences`:
|
576
866
|
|
577
867
|
```ruby
|
@@ -591,6 +881,8 @@ This rewinds all registered sequences.
|
|
591
881
|
Traits
|
592
882
|
------
|
593
883
|
|
884
|
+
### Defining traits
|
885
|
+
|
594
886
|
Traits allow you to group attributes together and then apply them
|
595
887
|
to any factory.
|
596
888
|
|
@@ -598,25 +890,25 @@ to any factory.
|
|
598
890
|
factory :user, aliases: [:author]
|
599
891
|
|
600
892
|
factory :story do
|
601
|
-
title "My awesome story"
|
893
|
+
title { "My awesome story" }
|
602
894
|
author
|
603
895
|
|
604
896
|
trait :published do
|
605
|
-
published true
|
897
|
+
published { true }
|
606
898
|
end
|
607
899
|
|
608
900
|
trait :unpublished do
|
609
|
-
published false
|
901
|
+
published { false }
|
610
902
|
end
|
611
903
|
|
612
904
|
trait :week_long_publishing do
|
613
905
|
start_at { 1.week.ago }
|
614
|
-
end_at
|
906
|
+
end_at { Time.now }
|
615
907
|
end
|
616
908
|
|
617
909
|
trait :month_long_publishing do
|
618
910
|
start_at { 1.month.ago }
|
619
|
-
end_at
|
911
|
+
end_at { Time.now }
|
620
912
|
end
|
621
913
|
|
622
914
|
factory :week_long_published_story, traits: [:published, :week_long_publishing]
|
@@ -626,7 +918,9 @@ factory :story do
|
|
626
918
|
end
|
627
919
|
```
|
628
920
|
|
629
|
-
|
921
|
+
### As implicit attributes
|
922
|
+
|
923
|
+
Traits can be used as implicit attributes:
|
630
924
|
|
631
925
|
```ruby
|
632
926
|
factory :week_long_published_story_with_title, parent: :story do
|
@@ -636,28 +930,33 @@ factory :week_long_published_story_with_title, parent: :story do
|
|
636
930
|
end
|
637
931
|
```
|
638
932
|
|
933
|
+
Note that defining traits as implicit attributes will not work if you have a
|
934
|
+
factory or sequence with the same name as the trait.
|
935
|
+
|
936
|
+
### Attribute precedence
|
937
|
+
|
639
938
|
Traits that define the same attributes won't raise AttributeDefinitionErrors;
|
640
939
|
the trait that defines the attribute latest gets precedence.
|
641
940
|
|
642
941
|
```ruby
|
643
942
|
factory :user do
|
644
|
-
name "Friendly User"
|
943
|
+
name { "Friendly User" }
|
645
944
|
login { name }
|
646
945
|
|
647
946
|
trait :male do
|
648
|
-
name
|
649
|
-
gender "Male"
|
947
|
+
name { "John Doe" }
|
948
|
+
gender { "Male" }
|
650
949
|
login { "#{name} (M)" }
|
651
950
|
end
|
652
951
|
|
653
952
|
trait :female do
|
654
|
-
name
|
655
|
-
gender "Female"
|
953
|
+
name { "Jane Doe" }
|
954
|
+
gender { "Female" }
|
656
955
|
login { "#{name} (F)" }
|
657
956
|
end
|
658
957
|
|
659
958
|
trait :admin do
|
660
|
-
admin true
|
959
|
+
admin { true }
|
661
960
|
login { "admin-#{name}" }
|
662
961
|
end
|
663
962
|
|
@@ -666,39 +965,44 @@ factory :user do
|
|
666
965
|
end
|
667
966
|
```
|
668
967
|
|
669
|
-
|
968
|
+
### In child factories
|
969
|
+
|
970
|
+
You can override individual attributes granted by a trait in a child factory:
|
670
971
|
|
671
972
|
```ruby
|
672
973
|
factory :user do
|
673
|
-
name "Friendly User"
|
974
|
+
name { "Friendly User" }
|
674
975
|
login { name }
|
675
976
|
|
676
977
|
trait :male do
|
677
|
-
name
|
678
|
-
gender "Male"
|
978
|
+
name { "John Doe" }
|
979
|
+
gender { "Male" }
|
679
980
|
login { "#{name} (M)" }
|
680
981
|
end
|
681
982
|
|
682
983
|
factory :brandon do
|
683
984
|
male
|
684
|
-
name "Brandon"
|
985
|
+
name { "Brandon" }
|
685
986
|
end
|
686
987
|
end
|
687
988
|
```
|
688
989
|
|
689
|
-
|
990
|
+
### Using traits
|
991
|
+
|
992
|
+
Traits can also be passed in as a list of symbols when you construct an instance
|
993
|
+
from factory\_bot.
|
690
994
|
|
691
995
|
```ruby
|
692
996
|
factory :user do
|
693
|
-
name "Friendly User"
|
997
|
+
name { "Friendly User" }
|
694
998
|
|
695
999
|
trait :male do
|
696
|
-
name
|
697
|
-
gender "Male"
|
1000
|
+
name { "John Doe" }
|
1001
|
+
gender { "Male" }
|
698
1002
|
end
|
699
1003
|
|
700
1004
|
trait :admin do
|
701
|
-
admin true
|
1005
|
+
admin { true }
|
702
1006
|
end
|
703
1007
|
end
|
704
1008
|
|
@@ -714,10 +1018,10 @@ the number of instances to create/build as second parameter, as documented in th
|
|
714
1018
|
|
715
1019
|
```ruby
|
716
1020
|
factory :user do
|
717
|
-
name "Friendly User"
|
1021
|
+
name { "Friendly User" }
|
718
1022
|
|
719
1023
|
trait :admin do
|
720
|
-
admin true
|
1024
|
+
admin { true }
|
721
1025
|
end
|
722
1026
|
end
|
723
1027
|
|
@@ -725,14 +1029,16 @@ end
|
|
725
1029
|
create_list(:user, 3, :admin, :male, name: "Jon Snow")
|
726
1030
|
```
|
727
1031
|
|
1032
|
+
### With associations
|
1033
|
+
|
728
1034
|
Traits can be used with associations easily too:
|
729
1035
|
|
730
1036
|
```ruby
|
731
1037
|
factory :user do
|
732
|
-
name "Friendly User"
|
1038
|
+
name { "Friendly User" }
|
733
1039
|
|
734
1040
|
trait :admin do
|
735
|
-
admin true
|
1041
|
+
admin { true }
|
736
1042
|
end
|
737
1043
|
end
|
738
1044
|
|
@@ -748,10 +1054,10 @@ When you're using association names that're different than the factory:
|
|
748
1054
|
|
749
1055
|
```ruby
|
750
1056
|
factory :user do
|
751
|
-
name "Friendly User"
|
1057
|
+
name { "Friendly User" }
|
752
1058
|
|
753
1059
|
trait :admin do
|
754
|
-
admin true
|
1060
|
+
admin { true }
|
755
1061
|
end
|
756
1062
|
end
|
757
1063
|
|
@@ -765,6 +1071,8 @@ end
|
|
765
1071
|
create(:post).author
|
766
1072
|
```
|
767
1073
|
|
1074
|
+
### Traits within traits
|
1075
|
+
|
768
1076
|
Traits can be used within other traits to mix in their attributes.
|
769
1077
|
|
770
1078
|
```ruby
|
@@ -780,13 +1088,15 @@ factory :order do
|
|
780
1088
|
end
|
781
1089
|
```
|
782
1090
|
|
1091
|
+
### With transient attributes
|
1092
|
+
|
783
1093
|
Finally, traits can accept transient attributes.
|
784
1094
|
|
785
1095
|
```ruby
|
786
1096
|
factory :invoice do
|
787
1097
|
trait :with_amount do
|
788
1098
|
transient do
|
789
|
-
amount 1
|
1099
|
+
amount { 1 }
|
790
1100
|
end
|
791
1101
|
|
792
1102
|
after(:create) do |invoice, evaluator|
|
@@ -798,9 +1108,103 @@ end
|
|
798
1108
|
create :invoice, :with_amount, amount: 2
|
799
1109
|
```
|
800
1110
|
|
1111
|
+
### Enum traits
|
1112
|
+
|
1113
|
+
Given an Active Record model with an enum attribute:
|
1114
|
+
|
1115
|
+
```rb
|
1116
|
+
class Task < ActiveRecord::Base
|
1117
|
+
enum status: {queued: 0, started: 1, finished: 2}
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
```
|
1121
|
+
|
1122
|
+
factory\_bot will automatically define traits for each possible value of the
|
1123
|
+
enum:
|
1124
|
+
|
1125
|
+
```rb
|
1126
|
+
FactoryBot.define do
|
1127
|
+
factory :task
|
1128
|
+
end
|
1129
|
+
|
1130
|
+
FactoryBot.build(:task, :queued)
|
1131
|
+
FactoryBot.build(:task, :started)
|
1132
|
+
FactoryBot.build(:task, :finished)
|
1133
|
+
```
|
1134
|
+
|
1135
|
+
Writing the traits out manually would be cumbersome, and is not necessary:
|
1136
|
+
|
1137
|
+
```rb
|
1138
|
+
FactoryBot.define do
|
1139
|
+
factory :task do
|
1140
|
+
trait :queued do
|
1141
|
+
status { :queued }
|
1142
|
+
end
|
1143
|
+
|
1144
|
+
trait :started do
|
1145
|
+
status { :started }
|
1146
|
+
end
|
1147
|
+
|
1148
|
+
trait :finished do
|
1149
|
+
status { :finished }
|
1150
|
+
end
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
```
|
1154
|
+
|
1155
|
+
If automatically defining traits for enum attributes on every factory is not
|
1156
|
+
desired, it is possible to disable the feature by setting
|
1157
|
+
`FactoryBot.automatically_define_enum_traits = false`
|
1158
|
+
|
1159
|
+
In that case, it is still possible to explicitly define traits for an enum
|
1160
|
+
attribute in a particular factory:
|
1161
|
+
|
1162
|
+
```rb
|
1163
|
+
FactoryBot.automatically_define_enum_traits = false
|
1164
|
+
|
1165
|
+
FactoryBot.define do
|
1166
|
+
factory :task do
|
1167
|
+
traits_for_enum(:status)
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
```
|
1171
|
+
|
1172
|
+
It is also possible to use this feature for other enumerable values, not
|
1173
|
+
specifically tied to Active Record enum attributes.
|
1174
|
+
|
1175
|
+
With an array:
|
1176
|
+
|
1177
|
+
```rb
|
1178
|
+
class Task
|
1179
|
+
attr_accessor :status
|
1180
|
+
end
|
1181
|
+
|
1182
|
+
FactoryBot.define do
|
1183
|
+
factory :task do
|
1184
|
+
traits_for_enum(:status, ["queued", "started", "finished"])
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
```
|
1188
|
+
|
1189
|
+
Or with a hash:
|
1190
|
+
|
1191
|
+
```rb
|
1192
|
+
class Task
|
1193
|
+
attr_accessor :status
|
1194
|
+
end
|
1195
|
+
|
1196
|
+
FactoryBot.define do
|
1197
|
+
factory :task do
|
1198
|
+
traits_for_enum(:status, { queued: 0, started: 1, finished: 2 })
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
```
|
1202
|
+
|
801
1203
|
Callbacks
|
802
1204
|
---------
|
803
1205
|
|
1206
|
+
### Default callbacks
|
1207
|
+
|
804
1208
|
factory\_bot makes available four callbacks for injecting some code:
|
805
1209
|
|
806
1210
|
* after(:build) - called after a factory is built (via `FactoryBot.build`, `FactoryBot.create`)
|
@@ -819,6 +1223,8 @@ end
|
|
819
1223
|
|
820
1224
|
Note that you'll have an instance of the user in the block. This can be useful.
|
821
1225
|
|
1226
|
+
### Multiple callbacks
|
1227
|
+
|
822
1228
|
You can also define multiple types of callbacks on the same factory:
|
823
1229
|
|
824
1230
|
```ruby
|
@@ -828,7 +1234,8 @@ factory :user do
|
|
828
1234
|
end
|
829
1235
|
```
|
830
1236
|
|
831
|
-
Factories can also define any number of the same kind of callback. These
|
1237
|
+
Factories can also define any number of the same kind of callback. These
|
1238
|
+
callbacks will be executed in the order they are specified:
|
832
1239
|
|
833
1240
|
```ruby
|
834
1241
|
factory :user do
|
@@ -839,9 +1246,12 @@ end
|
|
839
1246
|
|
840
1247
|
Calling `create` will invoke both `after_build` and `after_create` callbacks.
|
841
1248
|
|
842
|
-
Also, like standard attributes, child factories will inherit (and can also
|
1249
|
+
Also, like standard attributes, child factories will inherit (and can also
|
1250
|
+
define) callbacks from their parent factory.
|
843
1251
|
|
844
|
-
Multiple callbacks can be assigned to run a block; this is useful when building
|
1252
|
+
Multiple callbacks can be assigned to run a block; this is useful when building
|
1253
|
+
various strategies that run the same code (since there are no callbacks that are
|
1254
|
+
shared across all strategies).
|
845
1255
|
|
846
1256
|
```ruby
|
847
1257
|
factory :user do
|
@@ -851,6 +1261,8 @@ factory :user do
|
|
851
1261
|
end
|
852
1262
|
```
|
853
1263
|
|
1264
|
+
### Global callbacks
|
1265
|
+
|
854
1266
|
To override callbacks for all factories, define them within the
|
855
1267
|
`FactoryBot.define` block:
|
856
1268
|
|
@@ -860,12 +1272,14 @@ FactoryBot.define do
|
|
860
1272
|
after(:create) { |object| AuditLog.create(attrs: object.attributes) }
|
861
1273
|
|
862
1274
|
factory :user do
|
863
|
-
name "John Doe"
|
1275
|
+
name { "John Doe" }
|
864
1276
|
end
|
865
1277
|
end
|
866
1278
|
```
|
867
1279
|
|
868
|
-
|
1280
|
+
### Symbol#to_proc
|
1281
|
+
|
1282
|
+
You can call callbacks that rely on `Symbol#to_proc`:
|
869
1283
|
|
870
1284
|
```ruby
|
871
1285
|
# app/models/user.rb
|
@@ -888,17 +1302,18 @@ create(:user) # creates the user and confirms it
|
|
888
1302
|
Modifying factories
|
889
1303
|
-------------------
|
890
1304
|
|
891
|
-
If you're given a set of factories (say, from a gem developer) but want to
|
892
|
-
|
1305
|
+
If you're given a set of factories (say, from a gem developer) but want to
|
1306
|
+
change them to fit into your application better, you can modify that factory
|
1307
|
+
instead of creating a child factory and adding attributes there.
|
893
1308
|
|
894
1309
|
If a gem were to give you a User factory:
|
895
1310
|
|
896
1311
|
```ruby
|
897
1312
|
FactoryBot.define do
|
898
1313
|
factory :user do
|
899
|
-
full_name "John Doe"
|
1314
|
+
full_name { "John Doe" }
|
900
1315
|
sequence(:username) { |n| "user#{n}" }
|
901
|
-
password "password"
|
1316
|
+
password { "password" }
|
902
1317
|
end
|
903
1318
|
end
|
904
1319
|
```
|
@@ -908,10 +1323,10 @@ Instead of creating a child factory that added additional attributes:
|
|
908
1323
|
```ruby
|
909
1324
|
FactoryBot.define do
|
910
1325
|
factory :application_user, parent: :user do
|
911
|
-
full_name
|
1326
|
+
full_name { "Jane Doe" }
|
912
1327
|
date_of_birth { 21.years.ago }
|
913
|
-
gender
|
914
|
-
health
|
1328
|
+
gender { "Female" }
|
1329
|
+
health { 90 }
|
915
1330
|
end
|
916
1331
|
end
|
917
1332
|
```
|
@@ -921,10 +1336,10 @@ You could modify that factory instead.
|
|
921
1336
|
```ruby
|
922
1337
|
FactoryBot.modify do
|
923
1338
|
factory :user do
|
924
|
-
full_name
|
1339
|
+
full_name { "Jane Doe" }
|
925
1340
|
date_of_birth { 21.years.ago }
|
926
|
-
gender
|
927
|
-
health
|
1341
|
+
gender { "Female" }
|
1342
|
+
health { 90 }
|
928
1343
|
end
|
929
1344
|
end
|
930
1345
|
```
|
@@ -953,6 +1368,14 @@ To set the attributes for each of the factories, you can pass in a hash as you n
|
|
953
1368
|
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
|
954
1369
|
```
|
955
1370
|
|
1371
|
+
In order to set different attributes for each factory, these methods may be passed a block, with the factory and the index as parameters:
|
1372
|
+
|
1373
|
+
```ruby
|
1374
|
+
twenty_somethings = build_list(:user, 10) do |user, i|
|
1375
|
+
user.date_of_birth = (20 + i).years.ago
|
1376
|
+
end
|
1377
|
+
```
|
1378
|
+
|
956
1379
|
`build_stubbed_list` will give you fully stubbed out instances:
|
957
1380
|
|
958
1381
|
```ruby
|
@@ -975,7 +1398,7 @@ users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
|
|
975
1398
|
Linting Factories
|
976
1399
|
-----------------
|
977
1400
|
|
978
|
-
|
1401
|
+
factory\_bot allows for linting known factories:
|
979
1402
|
|
980
1403
|
```ruby
|
981
1404
|
FactoryBot.lint
|
@@ -1002,8 +1425,10 @@ namespace :factory_bot do
|
|
1002
1425
|
desc "Verify that all FactoryBot factories are valid"
|
1003
1426
|
task lint: :environment do
|
1004
1427
|
if Rails.env.test?
|
1005
|
-
|
1428
|
+
conn = ActiveRecord::Base.connection
|
1429
|
+
conn.transaction do
|
1006
1430
|
FactoryBot.lint
|
1431
|
+
raise ActiveRecord::Rollback
|
1007
1432
|
end
|
1008
1433
|
else
|
1009
1434
|
system("bundle exec rake factory_bot:lint RAILS_ENV='test'")
|
@@ -1015,8 +1440,7 @@ end
|
|
1015
1440
|
|
1016
1441
|
After calling `FactoryBot.lint`, you'll likely want to clear out the
|
1017
1442
|
database, as records will most likely be created. The provided example above
|
1018
|
-
uses
|
1019
|
-
gem to your Gemfile under the appropriate groups.
|
1443
|
+
uses an sql transaction and rollback to leave the database clean.
|
1020
1444
|
|
1021
1445
|
You can lint factories selectively by passing only factories you want linted:
|
1022
1446
|
|
@@ -1050,10 +1474,17 @@ You can also specify the strategy used for linting:
|
|
1050
1474
|
FactoryBot.lint strategy: :build
|
1051
1475
|
```
|
1052
1476
|
|
1477
|
+
Verbose linting will include full backtraces for each error, which can be
|
1478
|
+
helpful for debugging:
|
1479
|
+
|
1480
|
+
```ruby
|
1481
|
+
FactoryBot.lint verbose: true
|
1482
|
+
```
|
1483
|
+
|
1053
1484
|
Custom Construction
|
1054
1485
|
-------------------
|
1055
1486
|
|
1056
|
-
If you want to use
|
1487
|
+
If you want to use factory\_bot to construct an object where some attributes
|
1057
1488
|
are passed to `initialize` or if you want to do something other than simply
|
1058
1489
|
calling `new` on your build class, you can override the default behavior by
|
1059
1490
|
defining `initialize_with` on your factory. Example:
|
@@ -1072,7 +1503,7 @@ end
|
|
1072
1503
|
sequence(:email) { |n| "person#{n}@example.com" }
|
1073
1504
|
|
1074
1505
|
factory :user do
|
1075
|
-
name "Jane Doe"
|
1506
|
+
name { "Jane Doe" }
|
1076
1507
|
email
|
1077
1508
|
|
1078
1509
|
initialize_with { new(name) }
|
@@ -1081,7 +1512,7 @@ end
|
|
1081
1512
|
build(:user).name # Jane Doe
|
1082
1513
|
```
|
1083
1514
|
|
1084
|
-
Although
|
1515
|
+
Although factory\_bot is written to work with ActiveRecord out of the box, it
|
1085
1516
|
can also work with any Ruby class. For maximum compatibility with ActiveRecord,
|
1086
1517
|
the default initializer builds all instances by calling `new` on your build class
|
1087
1518
|
without any arguments. It then calls attribute writer methods to assign all the
|
@@ -1092,7 +1523,7 @@ You can override the initializer in order to:
|
|
1092
1523
|
|
1093
1524
|
* Build non-ActiveRecord objects that require arguments to `initialize`
|
1094
1525
|
* Use a method other than `new` to instantiate the instance
|
1095
|
-
* Do
|
1526
|
+
* Do wild things like decorate the instance after it's built
|
1096
1527
|
|
1097
1528
|
When using `initialize_with`, you don't have to declare the class itself when
|
1098
1529
|
calling `new`; however, any other class methods you want to call will have to
|
@@ -1102,7 +1533,7 @@ For example:
|
|
1102
1533
|
|
1103
1534
|
```ruby
|
1104
1535
|
factory :user do
|
1105
|
-
name "John Doe"
|
1536
|
+
name { "John Doe" }
|
1106
1537
|
|
1107
1538
|
initialize_with { User.build_with_name(name) }
|
1108
1539
|
end
|
@@ -1114,7 +1545,7 @@ by calling `attributes`:
|
|
1114
1545
|
```ruby
|
1115
1546
|
factory :user do
|
1116
1547
|
transient do
|
1117
|
-
comments_count 5
|
1548
|
+
comments_count { 5 }
|
1118
1549
|
end
|
1119
1550
|
|
1120
1551
|
name "John Doe"
|
@@ -1125,7 +1556,7 @@ end
|
|
1125
1556
|
|
1126
1557
|
This will build a hash of all attributes to be passed to `new`. It won't
|
1127
1558
|
include transient attributes, but everything else defined in the factory will be
|
1128
|
-
passed (associations,
|
1559
|
+
passed (associations, evaluated sequences, etc.)
|
1129
1560
|
|
1130
1561
|
You can define `initialize_with` for all factories by including it in the
|
1131
1562
|
`FactoryBot.define` block:
|
@@ -1154,7 +1585,7 @@ build(:user)
|
|
1154
1585
|
User.new('value')
|
1155
1586
|
```
|
1156
1587
|
|
1157
|
-
This prevents duplicate assignment; in versions of
|
1588
|
+
This prevents duplicate assignment; in versions of factory\_bot before 4.0, it
|
1158
1589
|
would run this:
|
1159
1590
|
|
1160
1591
|
```ruby
|
@@ -1287,7 +1718,7 @@ FactoryBot.define do
|
|
1287
1718
|
|
1288
1719
|
|
1289
1720
|
factory :user do
|
1290
|
-
name "John Doe"
|
1721
|
+
name { "John Doe" }
|
1291
1722
|
end
|
1292
1723
|
end
|
1293
1724
|
```
|
@@ -1340,13 +1771,13 @@ with associations, as below:
|
|
1340
1771
|
|
1341
1772
|
```ruby
|
1342
1773
|
FactoryBot.define do
|
1343
|
-
factory :united_states, class: Location do
|
1344
|
-
name 'United States'
|
1774
|
+
factory :united_states, class: "Location" do
|
1775
|
+
name { 'United States' }
|
1345
1776
|
association :location_group, factory: :north_america
|
1346
1777
|
end
|
1347
1778
|
|
1348
|
-
factory :north_america, class: LocationGroup do
|
1349
|
-
name 'North America'
|
1779
|
+
factory :north_america, class: "LocationGroup" do
|
1780
|
+
name { 'North America' }
|
1350
1781
|
end
|
1351
1782
|
end
|
1352
1783
|
```
|
@@ -1378,7 +1809,7 @@ require 'factory_bot'
|
|
1378
1809
|
```
|
1379
1810
|
|
1380
1811
|
Once required, assuming you have a directory structure of `spec/factories` or
|
1381
|
-
`test/factories`, all you'll need to do is run
|
1812
|
+
`test/factories`, all you'll need to do is run:
|
1382
1813
|
|
1383
1814
|
```ruby
|
1384
1815
|
FactoryBot.find_definitions
|
@@ -1400,7 +1831,7 @@ require 'factory_bot'
|
|
1400
1831
|
|
1401
1832
|
FactoryBot.define do
|
1402
1833
|
factory :user do
|
1403
|
-
name 'John Doe'
|
1834
|
+
name { 'John Doe' }
|
1404
1835
|
date_of_birth { 21.years.ago }
|
1405
1836
|
end
|
1406
1837
|
end
|