Default `use_parent_strategy` to true
Background --- In issues #64 and #66 (from 2010) people expressed surprise that using the `build` strategy would still `create` any associations. I remember being similarly surprised by that behavior when I first started using factory_bot. The main reason for this behavior is to ensure the built instance will be valid if there were any foreign key validations (like `validates_presence_of :user_id`). If we don't save the associations, there won't be any ids present. PR #191 (from 2011) offers a workaround for people who don't want records to be saved automatically. Passing `strategy: :build` (originally `method: :build`, but later renamed) when declaring the association will prevent it from being saved when using `build`. But #191 isn't really a complete solution (as discussed on the PR after it was merged). `strategy: :build` may do the right thing when building objects with the `build` strategy, but it can cause new problems when using the `create` strategy. A better solution would be something like: `strategy: <whatever strategy I was already using>`. PRs #749 and #961 (merged in 2016) introduce something like that, with the `use_parent_strategy` configuration option. With this turned on `build` end up being generally [a little faster][] than `build_stubbed`, since it no longer needs to hit the database for each association. [a little faster]: https://gist.github.com/composerinteralia/d4796df9140f431e36f88dfb6fe9733a I have set `use_parent_strategy` on several projects now. I also added it to suspenders in thoughtbot/suspenders#952. On newer projects I have not run into any problems. On existing projects I have seen the occasional test failure, which are easy enough to fix by changing `build` to `create`. Unfortunately I don't think `use_parent_strategy` is widely known, since it wasn't documented until #1234 (about a month ago). I also learned in #1234 that the `use_parent_strategy` setting gets wiped out with `FactoryBot.reload`, which can be problematic when using factory_bot_rails. To summarize, we have been exploring having a `build` strategy that uses `build` all the way down since 2010, but there still isn't a totally reliable way to get that. This PR --- * Default to using the parent strategy for factory_bot 5, as suggested in #749. * In the original PR for this (#1240) I had removed `use_parent_strategy`, but to make the transition smoother I ended up fixing the reload problem in #1244 Once factory\_bot 5 is released and the dust has settled, it may make sense to deprecate `use_parent_strategy`, and maybe also the strategy option for associations.
This commit is contained in:
parent
3e1a941347
commit
d0208eda9c
|
@ -347,9 +347,35 @@ factory :post do
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
The behavior of the association method varies depending on the build strategy used for the parent object.
|
In factory\_bot 5, associations default to using the same build strategy as
|
||||||
|
their parent object:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
FactoryBot.define do
|
||||||
|
factory :author
|
||||||
|
|
||||||
|
factory :post do
|
||||||
|
author
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post = build(:post)
|
||||||
|
post.new_record? # => true
|
||||||
|
post.author.new_record? # => true
|
||||||
|
|
||||||
|
post = create(:post)
|
||||||
|
post.new_record? # => false
|
||||||
|
post.author.new_record? # => false
|
||||||
|
```
|
||||||
|
|
||||||
|
This is different than the default behavior for previous versions of
|
||||||
|
factory\_bot, where the association strategy would not always match the strategy
|
||||||
|
of the parent object. If you want to continue using the old behavior, you can
|
||||||
|
set the `use_parent_strategy` configuration option to `false`.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
FactoryBot.use_parent_strategy = false
|
||||||
|
|
||||||
# Builds and saves a User and a Post
|
# Builds and saves a User and a Post
|
||||||
post = create(:post)
|
post = create(:post)
|
||||||
post.new_record? # => false
|
post.new_record? # => false
|
||||||
|
@ -364,6 +390,8 @@ post.author.new_record? # => false
|
||||||
To not save the associated object, specify strategy: :build in the factory:
|
To not save the associated object, specify strategy: :build in the factory:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
FactoryBot.use_parent_strategy = false
|
||||||
|
|
||||||
factory :post do
|
factory :post do
|
||||||
# ...
|
# ...
|
||||||
association :author, factory: :user, strategy: :build
|
association :author, factory: :user, strategy: :build
|
||||||
|
@ -384,28 +412,6 @@ factory :post do
|
||||||
author strategy: :build # <<< this does *not* work; causes author_id to be nil
|
author strategy: :build # <<< this does *not* work; causes author_id to be nil
|
||||||
```
|
```
|
||||||
|
|
||||||
To have unspecified associations use the parent's strategy, instead of using :create, you can use the configuration `use_parent_strategy`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
FactoryBot.use_parent_strategy = true
|
|
||||||
|
|
||||||
factory :post do
|
|
||||||
# ...
|
|
||||||
author
|
|
||||||
end
|
|
||||||
|
|
||||||
post = build(:post)
|
|
||||||
post.new_record? # => true
|
|
||||||
post.author.new_record? # => true
|
|
||||||
|
|
||||||
post = create(:post)
|
|
||||||
post.new_record? # => false
|
|
||||||
post.author.new_record? # => false
|
|
||||||
```
|
|
||||||
|
|
||||||
If you are using rspec, you can set this configuration in your spec_helper.rb (or rails_helper.rb, if using Rails).
|
|
||||||
If you want to set it globally, you can use an intializer (if using Rails).
|
|
||||||
|
|
||||||
Generating data for a `has_many` relationship is a bit more involved,
|
Generating data for a `has_many` relationship is a bit more involved,
|
||||||
depending on the amount of flexibility desired, but here's a surefire example
|
depending on the amount of flexibility desired, but here's a surefire example
|
||||||
of generating associated data.
|
of generating associated data.
|
||||||
|
|
|
@ -55,7 +55,7 @@ module FactoryBot
|
||||||
@configuration = nil
|
@configuration = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
mattr_accessor :use_parent_strategy, instance_accessor: false, default: false
|
mattr_accessor :use_parent_strategy, instance_accessor: false, default: true
|
||||||
|
|
||||||
# Look for errors in factories and (optionally) their traits.
|
# Look for errors in factories and (optionally) their traits.
|
||||||
# Parameters:
|
# Parameters:
|
||||||
|
|
|
@ -20,7 +20,7 @@ RSpec.configure do |config|
|
||||||
|
|
||||||
config.before do
|
config.before do
|
||||||
FactoryBot.reload
|
FactoryBot.reload
|
||||||
FactoryBot.use_parent_strategy = false
|
FactoryBot.use_parent_strategy = true
|
||||||
end
|
end
|
||||||
|
|
||||||
config.after do
|
config.after do
|
||||||
|
|
Loading…
Reference in New Issue