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

before(:create) not executed in expected order #1573

Open
samanthawritescode opened this issue May 1, 2023 · 2 comments
Open

before(:create) not executed in expected order #1573

samanthawritescode opened this issue May 1, 2023 · 2 comments
Labels

Comments

@samanthawritescode
Copy link

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

@J-Swift
Copy link

J-Swift commented Jan 10, 2024

I think I'm running into something similar, where factory use in a before(:all) block fails because http calls arent being stubbed. Nothing to add other than that so far 😅

@milgner
Copy link

milgner commented Feb 26, 2024

In my project, before(:create) doesn't seem to be run at all. This leads to a situation where

FactoryBot.build(:my_factory).save!

works - the after(:build) callback runs as expected and fills in some bidirectional associations for delegated types correctly.

But

FactoryBot.create(:my_factory)

throws

Validation failed: Data source can't be blank (ActiveRecord::RecordInvalid)

because neither after(:build) nor before(:create) are invoked. This is contrary to the documentation which suggests that FactoryBot.create should run both after(:build) and before(:create) callbacks.

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