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