thoughtbot / thoughtbot/factory_bot
`before(:create)` not executed in expected order
Nobody has claimed this yet.
- Dominant language
- Ruby
- Stars
- 8.2k
- Forks
- 2.6k
- PR merge metrics
- No merged PRs in 30d
Description
Description
The before(:create) block is not getting executed in the order I expect when using instance in one of the factory's associations.
I'm trying to update an existing factory I have (organization) to allow a :build strategy. It currently makes use of a block like this:
after(:create) do |instance, evaluator|
instance.update!(
account_type: evaluator.account_type || build(:account_type, organization: instance),
)
end
Which obviously won't work when using build. So I've updated it by making an association:
account_type { association :account_type, organization: instance }
But now I can't find evidence that the factory's before(:create) block that I need to run is running (at least in the order I expect). See repro steps below.
Reproduction Steps
This fails because the http call is not getting stubbed out:
require "bundler/inline"
gemfile(true) do
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
gem "factory_bot", "~> 6.0"
gem "activerecord"
gem "sqlite3"
gem 'webmock'
end
require "active_record"
require "factory_bot"
require "minitest/autorun"
require "logger"
require 'uri'
require 'net/http'
require 'webmock/minitest'
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :organizations, force: true do |t|
t.integer :account_type_id
end
create_table :account_types, force: true do |t|
t.integer :organization_id
end
end
class Organization < ActiveRecord::Base
belongs_to :account_type, optional: true
before_create :get_nasa_image
# [callback] before_create
def get_nasa_image
uri = URI('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY')
Net::HTTP.get_response(uri)
end
end
class AccountType < ActiveRecord::Base
belongs_to :organization
end
FactoryBot.define do
factory :organization do
account_type { association :account_type, organization: instance }
before(:create) do
WebMock.stub_request(:get, "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY").
with(
headers: {
'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Host'=>'api.nasa.gov',
'User-Agent'=>'Ruby'
}).
to_return(status: 200, body: "", headers: {})
end
end
factory :account_type do
organization
end
end
class FactoryBotTest < Minitest::Test
def test_factory_bot_stuff
org = FactoryBot.create(:organization)
assert true
end
end
# Run the tests with `ruby <filename>`
However, if you change the organization factory to this, the http call does in fact get stubbed out and everything works as expected:
factory :organization do
before(:create) do
WebMock.stub_request(:get, "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY").
with(
headers: {
'Accept'=>'*/*',
'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Host'=>'api.nasa.gov',
'User-Agent'=>'Ruby'
}).
to_return(status: 200, body: "", headers: {})
end
after(:create) do |instance, evaluator|
instance.update!(
account_type: evaluator.account_type || build(:account_type, organization: instance),
)
end
end
Expected behavior
I expected the factory's before(:create) block to run before the model's after(:create) block.
Actual behavior
The factory's before(:create) block is not run, the HTTP call is not stubbed out, and the test fails because the model is running it's after(:create).
System configuration
factory_bot version: 6.2.1
rails version: 6.1.7.3
ruby version: 2.7.8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the inline reproduction and inspect FactoryBot's callback and association execution path, focusing on how association :account_type, organization: instance affects callback ordering. The work is done when the factory's before(:create) stub runs before the model callback and the reproduction passes.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rails, ruby
- Domain
- testing
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100