Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Associations blocks are being executed with invalid data at attributes_for #1589

Open
cesarjr opened this issue Aug 21, 2023 · 2 comments
Open
Labels

Comments

@cesarjr
Copy link

cesarjr commented Aug 21, 2023

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.

@cesarjr cesarjr added the bug label Aug 21, 2023
@ellnix
Copy link

ellnix commented Oct 1, 2023

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

@ezekg
Copy link

ezekg commented Dec 15, 2023

Do you think it'd make sense for factory_bot to infer associations where possible?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants