doorkeeper: FactoryBot doesn't see Doorkeeper classes

good day for everyone when using doorkeeper-4.3.0, a have a problem with FactoryBot. He refused to see doorkeeper classes:

NameError:
  uninitialized constant Doorkeeper::AccessToken
NameError:
  uninitialized constant Doorkeeper::Application

and other rollback to 4.2.6 solve this problem

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 2
  • Comments: 21 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I was able to get around this issue by putting quotes around the class name when defining the factory:

factory :access_token, class: 'Doorkeeper::AccessToken' do
  ...
end

High five to @sman591 for this excellent commit message:

As described in doorkeeper-gem#1043,
factories defined by doorkeeper are unable to be loaded by host apps
using the `factory_bot_rails` gem (receiving errors such as
`uninitialized constant Doorkeeper::AccessGrant`)

Documentation for factory_bot was later added to explain an easy fix for
this: instead of defining the factory's `class: ` with a named constant,
wrap the constant in a string. By doing so, the constant isn't evaluated
until the factory actually runs, at which point the `Doorkeeper::` models
have all been loaded.

I tried this, will work for you until this issue is solved on factory_bot_rails. Create the file lib/factory_bot_rails/railtie.rb on your project - we’re actually copying the current railtie contents and modifying one line:

require 'factory_bot'
require 'factory_bot_rails/generator'
require 'rails'

module FactoryBot
  class Railtie < Rails::Railtie

    initializer "factory_bot.set_fixture_replacement" do
      FactoryBotRails::Generator.new(config).run
    end

    initializer "factory_bot.set_factory_paths" do
      FactoryBot.definition_file_paths = [
        Rails.root.join('factories'),
        Rails.root.join('test', 'factories'),
        Rails.root.join('spec', 'factories')
      ]
    end

    config.after_initialize do
      # This line is the one that fixes it:
      ActiveSupport.on_load(:active_record) { FactoryBot.find_definitions }

      if defined?(Spring)
        Spring.after_fork { FactoryBot.reload }
      end
    end
  end
end

I’ll post this as a PR on Factory Bot Rails repo

So because of it is not a bug in Doorkeeper itself I will close this issue now. The only solution I see (for Doorkeeper) is to manually trigger AR::Base (before factory bot rails initializer), but you are right - I don’t think I would do this for some testing gem.

As factory_bot_rails is just about 8 lines of code - I don’t think it is a big problem to replace it with native factory_bot. But if somebody will see this issue and want to propose some solution to fix it - feel free to send a PR 😃

Hi @slim1979 . The problem is in factory_bot_rails gem. It has an railtie that tries to find factory definitions (and load them) after engine initialization, and this cause the error because models is not loaded yet, the same as ActiveRecord.

To solve this problem you can replace factory_bot_rails gem with just factory_bot and everything will work (I tried for your branch).

Related issue exists on the factory_bot_rails gem itself https://github.com/thoughtbot/factory_bot_rails/issues/134 , but it is closed and nobody seems to fix it.

P.S. I recommend you to move require statements in your rails_helper.rb (like 'capybara/rspec' or 'shoulda/matchers') under require 'rspec/rails' as it suggested by the official RSpec gem docs (you can even find the corresponding comment # Add additional requires below this line. Rails is not loaded until this point!). And you don’t need to require ‘doorkeeper’ as it would be loaded by your app (require File.expand_path('../../config/environment', __FILE__))

He is right about the helpers. Doorkeeper models must be loaded at the time when ActiveRecord are loaded using ActiveSupport lazy hooks (in this case we can configure some AR options before Doorkeeper models would be loaded). ActiveRecord::Base has autoloading and would be initialized only on first call. When tests are invoked, factories run before the ActiveRecord loaded (because nobody touches ActiveRecord::Base, so it is not loaded yet), so this is why we get this error (on this moment Doorkeeper::AccessToken and other models still not required).

I’l try to investigate the problem and solve it for 4.3.1 release.

And what about yhe factories?

Do you have

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods

  config.before(:suite) do
    FactoryBot.find_definitions
  end
end

?