factory_bot
factory_bot copied to clipboard
Associations blocks are being executed with invalid data at attributes_for
Description
Hi team!
I noticed that FactoryBot is executing the associations block even when I'm using attributes_for
.
When I have one block which depends on another association, ruby is going to generate an undefined method "the-association-name" for nil:NilClass
.
I expected that the associations blocks weren't executed.
Thanks. You're great 💜!
Reproduction Steps
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"
end
require "active_record"
require "factory_bot"
require "minitest/autorun"
require "logger"
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :blogs, force: true do |t|
t.string :name
end
create_table :authors, force: true do |t|
t.string :name
t.references :blog
end
create_table :posts, force: true do |t|
t.string :body
t.references :blog
t.references :author
end
end
class Blog < ActiveRecord::Base
has_many :authors
has_many :blogs
end
class Author < ActiveRecord::Base
belongs_to :blog
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :blog
belongs_to :author
end
FactoryBot.define do
factory :blog do
name { "the blog name" }
end
factory :author do
name { "the author name" }
end
factory :post do
body { "the post body" }
blog
# This block should not be executed
# on FactoryBot.attributes_for(:comment)
author { blog.authors.first }
end
end
class FactoryBotTest < Minitest::Test
def test_factory_bot_stuff
post = FactoryBot.create(:post)
assert_equal post.author, post.blog.authors.first
# true
FactoryBot.attributes_for(:post)
# NoMethodError: undefined method `authors' for nil:NilClass
end
end
Expected behavior
I expected that the associations blocks weren't executed or the associations data were valids.
Actual behavior
The associations blocks are being executed with invalid data.
Unfortunately, giving a block to author
means it will be recognized as a dynamic attribute, not an association (internally therefore there will be no distinction between body
and author
, they are simply fields FactoryBot populates by running a block).
In addition, there is a deep rooted assumption in the code base that an association cannot be passed a block, it can only have traits and attribute overrides.
Maybe this post
factory could work for you?
factory :post do
body { "the post body" }
author
blog { association :blog, authors: [author] }
end
Do you think it'd make sense for factory_bot
to infer associations where possible?