thoughtbot / thoughtbot/factory_bot

Associations blocks are being executed with invalid data at attributes_for

Open
#1,589 2 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Ruby
Stars
8.2k
Forks
2.6k
PR merge metrics
No merged PRs in 30d

Description

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.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the supplied ActiveRecord/SQLite reproduction and trace FactoryBot.attributes_for(:post), especially how the blog association and author block are evaluated. Add a regression test for the expected handling of association blocks, then run the relevant test suite to confirm attributes_for no longer raises.

Written by the indexing model from the issue text.

Assessment

Tech stack
rails, ruby, sqlite
Domain
testing
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.