thoughtbot / factory_bot_rails

Factory Bot ♥ Rails

Home Page:https://thoughtbot.com/services/ruby-on-rails

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Factory not registered: "credit_card"

vassalloandrea opened this issue · comments

I'm using Solidus to build an e-commerce.
Updating factory_bot_rails from the version 4.11.1 to the latest 5.0.2 something broke.

Running the test locally, RSpec returns this error:

KeyError:
  Factory not registered: "credit_card"

In this case, I included the credit_card factory from Solidus and overrode it adding a cc_type.

My override: spec/factories/credit_card_factory.rb

# frozen_string_literal: true

require 'spree/testing_support/factories/credit_card_factory'

FactoryBot.modify do
  factory :credit_card, class: Spree::CreditCard do
    cc_type { 'Visa' }
  end
end

Solidus factory: here the link

# frozen_string_literal: true

FactoryBot.define do
  factory :credit_card, class: 'Spree::CreditCard' do
    verification_value { 123 }
    month { 12 }
    year { 1.year.from_now.year }
    number { '4111111111111111' }
    name { 'Spree Commerce' }
    association(:payment_method, factory: :credit_card_payment_method)
    association(:address)

    trait :failing do
      number { "0000000000000000" }
    end
  end
end

I tried to figure out without success.
For now, I blocked the version on the project to factory_bot_rails 4.11.1.

+1

Same situation with solidus 2.9.1 and factory_bot_rails 5.0.2

@vassalloandrea I think it's a "chicken and egg" problem. I fixed the problem by moving out the file where I use FactoryBot.modify from spec/factories into another folder which is not autorequired from factory_bot, like spec/modified_spree_factories, then I manually require all files inside this new folder:

Inside rails_helper.rb

require 'spree/testing_support/factories'
Dir[Rails.root.join('spec', 'modified_spree_factories', '**', '*.rb')].sort.each { |file| require file }

Hope it helps!

This has to do with FactoryBot.reload not playing well with requires in the definitions. FactoryBot.reload wipes out all of the factory definitions (including the spree definitions), and then reloads any definition files it knows about using load. If the spree definitions have already been required, require is a no-op the second time around.

This has always been a problem when using spring, since spring triggers a FactoryBot.reload in its forked process. It worked in 4.11 without spring because the definitions were only ever loaded once (unless you manually called FactoryBot.reload). This broke in 5.0 because there was a bug causing us to reload the definitions twice. We fixed the double reloading in 1e55d45, so if you upgrade to >= 5.1.1 the problem should go away (assuming you don't use spring).

It would be nice if we could get this use case working with spring, but I think that would require changes on the solidus side. We have an official way of sharing factories in the README, but solidus is not sharing them like that. You could add something like:

    config.factory_bot.definition_file_paths.unshift(
      Gem::Specification.find_by_name("solidus_core").gem_dir +
      "/lib/spree/testing_support/factories/credit_card_factory"
    )

to your application config to let factory_bot handle reloading the definitions as necessary, but unfortunately you can't do that with the whole "/lib/spree/testing_support/factories" directory because some of the factories there require other factories. You would have to configure them in whatever order solidus expects.

Another approach would be to set config.factory_bot.definition_file_paths = [] so factory_bot_rails doesn't handle any of the definition loading at all and then manually require the files you want in the order you want them. (This is effectively what @delphaber did by moving things into a directory factory_bot_rails doesn't know about.)

I will probably close this issue, since factory_bot_rails is back to the old behavior, and we do have a documented way of sharing factories.

Thank you very much for the explanation @composerinteralia !

@composerinteralia , I'm getting the Factory not registered error in Rails 6, and I'm not using Solidus. I'm not using rspec, so it's just the default minitest that comes with Rails.

The factory is pretty simple and defined in test/factories.rb:

FactoryBot.define do
  sequence :email do |n|
    "user#{n}@example.com"
  end

  factory :user do
    email
    password { "password123" }
  end
end

Should I create a separate issue?

If I add FactoryBot.reload in test_helper.rb, I don't get the error:

class ActiveSupport::TestCase
  [... redacted ...]
  FactoryBot.reload
  include FactoryBot::Syntax::Methods
end

@mikong If you don't have solidus anywhere in your Gemfile.lock then this is a separate issue. I don't see anything obviously wrong in your code. Could you provide a sample application that reproduces the problem?

@composerinteralia The error isn't happening anymore in my application. I tried to replicate the error in a branch just after I added factory_bot_rails but it's also no longer happening. If I reproduce the error next time, I'll open a new issue with a link to a sample application. Thanks.

Sounds good. Thanks!

I am going to close this issue for now for the reasons mentioned in #341 (comment).

commented

This is still an issue in Rails 6, I had to add
FactoryBot.reload
after appending a new route in definition_file_paths array in my rails_helper.rb
that solved the issue for me

commented

Same here, rails 6, sometimes factories can't be found.
FactoryBot.reload suggestion solves the issue

Ran into this issue just now. In implementing the above, I was able to get my factories to work in the console, but unable to run my specs. I discovered that adding config.include FactoryBot::Syntax::Methods to my rails_helper.rb solved the issue for me and my specs now run! I found the suggestion in this thread.

This is still an issue in Rails 6, I had to add FactoryBot.reload after appending a new route in definition_file_paths array in my rails_helper.rb that solved the issue for me

Same here, rails 6, Factory not registered: "user"
FactoryBot.reload suggestion solves the issue

Thanks for supporting. It's worked for me <3

commented

Was stuck on the same issue for a while for rails 6.1.7.3 and factory_bot_rails 6.2.0 (not using Spring or Solidus). Turns out, I had the /factories folder in the wrong location, relative to the paths that FactoryBot was searching for loading. I did not need the explicit FactoryBot.reload code addition in rails_helper.rb or spec_helper.rb.

If you spin up rails c for your project and run FactoryBot.reload, you'll see the array of file paths that FactoryBot is looking for to find your factories. Mine was nested one level above all the paths, and I just had to move it so it complied with one of them and still made sense for my project structure.