user1185081
user1185081

Reputation: 2118

How to configure FactoryBot files path in a Rails engine?

For my first Ruby on Rails engine - named "glossary" - I wish to implement Rspec, ShouldaMatchers, FactoryBot as test suite. Things look good, but FactoryBot keeps claiming that factories are not registered:

Failures:

  1) Glossary::User   
     Failure/Error: subject {FactoryBot.build(:user)}

     ArgumentError:
       Factory not registered: user 

In the development process, rails generate model user first_name last_name user_name created the following files:

which looks good.

rails_helper.rb contains the following:

    ENV['RAILS_ENV'] ||= 'test'
    require File.expand_path('../dummy/config/environment.rb', __FILE__)

    require 'spec_helper'

    # Prevent database truncation if the environment is production
    abort("The Rails environment is running in production mode!") if Rails.env.production?
    require 'rspec/rails'
    require 'shoulda/matchers'
    require 'factory_bot_rails'
    FactoryBot.definition_file_paths << File.expand_path('../spec/factories', __FILE__)

---

    # Fred 2018-07-29: added to engine configuration
    Shoulda::Matchers.configure do |config|
      config.integrate do |with|
        # Choose a test framework:
        with.test_framework :rspec

        # Choose one or more libraries:
        with.library :rails
      end
    end

    # spec/support/factory_bot.rb
    RSpec.configure do |config|
      config.include FactoryBot::Syntax::Methods
    end

lib/glossary/engine.rb contains the following:

module Glossary
  class Engine < ::Rails::Engine
    isolate_namespace Glossary

    # Fred 2018-07-29: added to engine configuration
    config.generators do |g|
      g.test_framework :rspec
      g.fixture_replacement :factory_bot 
      g.factory_bot dir: 'spec/factories' 
    end
  end
end

FactoryBot generated this spec/factories/glossary_users.rb file:

FactoryBot.define do
  factory :glossary_user, class: 'Glossary::User' do
    first_name "MyString"
    last_name "MyString"
    user_name "MyString"
  end
end

which I refer to in the spec/models/glossary/user_spec.rb file:

require 'rails_helper'

module Glossary
  RSpec.describe User, type: :model do
    describe 'Validations'
    subject {FactoryBot.build(:user)}
      it { should validate_presence_of(:user_name) }

  end
end

Nevertheless, issuing the following command in the rails console does not return the expected path, and factories remain not found:

Fred:glossary$ rails console                                                                                                                 
Loading development environment (Rails 5.2.0)                                                                                                                   
irb(main):001:0> FactoryBot.find_definitions                                                                                                                    
=> ["/var/www/glossary/spec/dummy/factories", "/var/www/glossary/spec/dummy/test/factories", "/var/www/glossary/spec/dummy/spec/factories"]                     
irb(main):002:0>  

Did I miss something in the setup or configuration?

Thanks for your help!

Upvotes: 1

Views: 4129

Answers (3)

Allison
Allison

Reputation: 2343

This will make factories that you define in your engine available to the apps that depend on your engine.

In your lib/my_engine/engine.rb:

module MyEngine
  class Engine < ::Rails::Engine

    initializer :load_factories, after: 'factory_bot.set_factory_paths' do
      if defined?(FactoryBot) && defined?(Faker) && !Rails.env.production?
        FactoryBot.definition_file_paths.prepend(
          File.join(MyEngine.root, 'spec', 'factories')
        )
      end
    end
  end
end

I like to protect it with a conditional to ensure it only runs under the appropriate conditions. You can find other variations on this in the FactoryBot codebase.

Upvotes: 0

Rahul Ahuja
Rahul Ahuja

Reputation: 35

I had this similar problem, I rearranged my code as

# rails_helper.rb
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../../config/environment', __FILE__)
# for factory bot
require 'factory_bot_rails'

And my test.rb as

# test.rb
config.eager_load = false
# some config
config.factory_bot.definition_file_paths += ["<my path"]

Edit 1: Explanation So FactoryBot scans the directories present in FactoryBot.definition_file_paths and registers all the factories by invoking FactoryBot.find_definitions

The FactoryBot.find_definitions invocation occurs when we

require 'factory_bot_rails`

Hence the order of statements inside rails_helper.rb matters

Edit 2: The above solution disregards the typo in factory usage as pointed out by others

Upvotes: 1

Roman Kiselenko
Roman Kiselenko

Reputation: 44380

The Factory have name glossary_user and should used as FactoryBot.build(:glossary_user)

Upvotes: 0

Related Questions