factory_bot icon indicating copy to clipboard operation
factory_bot copied to clipboard

`modify` doesn't allow changing the class

Open jasonkarns opened this issue 3 years ago • 1 comments

Description

FactoryBot.modify doesn't allow changing a factory's class. It's not explicitly mentioned in the docs, so this could reasonably be classified as a feature request. Though I rather expected it to be possible.

Reproduction Steps

Given:

class Test; end

class TestNew; end

FactoryBot.define do
  factory :test do; end
end

FactoryBot.modify do
  factory :test, class: "TestNew" do; end
end

Expected behavior

I expected FactoryBot.build :test to return an instance of TestNew.

Actual behavior

[2] pry(main)> FactoryBot.build :test
=> #<Test:0x00007fe87d6c1ed0>

System configuration

factory_bot version: 5.1.1 rails version: 6.0 ruby version: 2.6.5

Rationale

I'm sure this probably seems like an odd thing to do. Here's the scenario: We are migrating models from a legacy database, so we have the legacy db models defined is Legacy::MyRecord where each (or most) of the legacy models has a corresponding MyRecord (no-namespace) which lives in the new database. The application code (including the existing factories) all reference Legacy::*.

We are using a feature flag to allow the code to run where each Legacy::* constant is instead redefined to be the new class. (We're using class Legacy::Foo < Foo to keep AR happy.) However, factory bot (and active record's) introspection doesn't allow the constant redefinition to "just work". For the AR associations to work, the factory bot factory needs to be of the right class.

There are dozens of legacy models to be migrated, which means dozens of factories that we'd prefer not to duplicate. The hope was that we could use .modify to change each factory's class when the feature flag is enabled. But this appears to have no effect.

jasonkarns avatar Dec 05 '20 15:12 jasonkarns

@jasonkarns I took an initial swing at this and I'm not sure how feasible this is from the feature request side. You might be better off using explicit class definition on all Factories and conditionally defining the class by the Feature Flag.

class Test; end

class TestNew; end

FactoryBot.define do
  factory :test, class: (feature_flag == 'legacy') ? "Test" : "TestNew" ; end
end

dudleysr avatar Feb 25 '21 03:02 dudleysr