Codementor Events

How to Setup RSpec, Factory Bot and Spring for a Rails 5 Engine

Published Dec 24, 2018
How to Setup RSpec, Factory Bot and Spring for a Rails 5 Engine

A lot of projects at MoneySmart(one of my past employers) are built using Rails Engines. If you're new to the concept of Rails engines in a few words they are basically minitature applications that promote modularization and reusability(these engines can be reused across multiple Rails apps) of code. For each of these projects, we've decided to write our tests using RSpec alongside Factory Bot to generate our test data. As we add more specs to a growing engine, we've also added Spring into the mix so that we have a faster running test suite overall.

One of the reasons behind this post is that setting up a Rails Engine with all the above wasn't straightforward and did require some additional looking up. Hopefully, the below steps should make it easier for you to get RSpec, Factory Bot and Spring setup within a new Rails 5 engine in no time:

Setting up RSpec

Step 1: Create a new engine that skips Test Unit and specifies where the Engine specific dummy application should be created when using RSpec

  • By default a new Rails engine comes with the test unit framework. So for starters, you need to create a new Rails engine with the command rails plugin new sample_engine --mountable --skip-test --dummy-path=spec/dummy.

    • If you observe closely, you may wonder what is this new command called --skip-test ? Many of you may be familiar with --skip-test-unit to skip test unit in your Rails apps. Turns out with Rails 5, as per this issue the command has now changed to --skip-test.

    • You need to also add --dummy-path=spec/dummy to the command that helps create a new Rails engine as this option specifies where the dummy application(which will be used for testing) should be generated. We specify the usage of spec folder here because we're making use of RSpec as our testing framework.

    • Using the mountable option tells the generator that you want a namespaced engine. You could read more on why one would want to consider using a mountable engine from what's specified in the Rails guides here.

Step 2: Add rspec-rails as a development dependency

  • Assuming you've already added Rails to your engines gemspec file, you now need to add rspec-rails as a development dependency within the same file. You can do so with the command s.add_development_dependency 'rspec-rails'. You can now run a bundle install from your engines root directory.

Step 3: Explicitly specify the usage of RSpec as the test framework

  • From the engine's root directory, you now need to navigate your way to the engine.rb(located at lib/engine_name/engine.rb) file and explicitly force the Rails engine to use the RSpec test framework with the below command. If we do not specify this change, all the generators that are used to create things like models etc., wrt the engine would actually create the relevant tests using Test Unit.
  config.generators do |g|
    g.test_framework :rspec
  end

Step 4: Run the RSpec generator command to get the basic RSpec setup files

  • You could then run the RSpec generator command(from your engines root directory) - rails g rspec:install so that you have the basic RSpec setup files that are required wrt your engine.

Side Note: Once you've completed step 4, you should now be able to run a command like rails g model Widget name:string and actually see a spec file created for you in the appropriate location. But you won't be able to run the spec yet.

Step 5: Get RSpec to load the Rails environment of the dummy app

  • By default RSpec would look for the environment file(used to load and initialize a Rails app) in a normal Rails app via the path specified in the statement - require File.expand_path('../../config/environment', __FILE__) as part of the spec/rails_helper.rb file. However, when using an engine, you'd need to specify the path of the environment file wrt your dummy app in order to get your engine specs to work. For this we need to replace the above line of code in your spec/rails_helper.rb with require File.expand_path('../dummy/config/environment.rb', __FILE__). Another thing that confirms that the engine specs work wrt the dummy app is if you see the test environment related migrations(on running a RAILS_ENV=test rake db:migrate wrt your engine), they are added to the schema.rb file within the dummy app.

Side note: The above steps are present in this PR if you'd like to see some running specs.

Setting up Factory Bot

Step 1: Add factory_bot_rails as a development dependency

  • For starters, you need to first add s.add_development_dependency 'factory_bot_rails' to your engine gemspec file and then run a bundle install

Step 2: Enable auto creation of new factories and specify the location as to where they should be created

  • You now need to add the below additonal code to your engine.rb so that whenever you try creating things like a new model etc., through a Rails generator command, you'd have the appropriate factories created for you automatically in the required location within the spec/factories directory. If you don't specify the path where you want the new factories to be created for you via - g.factory_bot dir: 'spec/factories', the engine would automatically create the new factories for you at the location test/factories/ .
module SampleEngine
  class Engine < ::Rails::Engine
    isolate_namespace SampleEngine

    config.generators do |g|
      g.test_framework :rspec
      g.fixture_replacement :factory_bot #newly added code
      g.factory_bot dir: 'spec/factories' #newly added code
    end
  end
end

Step 3: Require factory_bot_rails in order to make the FactoryBot command recognizable

  • Additionally, you need to specify require 'factory_bot_rails' in your rails_helper.rb else you will get an uninitialized constant related error when you try using a command like FactoryBot.create(:widget) inside any of your engine specs.

Step 4[Optional]: Allow accessing engine factories from within a project

  • There are times when you want your engine factories to be accessible in a project where the engine is mounted and inorder to do so you need to add the below code in your engine.rb file as mentioned in this issue on the factory_bot_rails repo. You're factories would be created by a standalone engine without the below code and thereby including the below code in your engine.rb is purely optional.
module SampleEngine
  class Engine < ::Rails::Engine
    #... existing code in `engine.rb`

    # new code
    initializer "sample_engine.factories", after: "factory_bot.set_factory_paths" do
      FactoryBot.definition_file_paths << File.expand_path('../../../spec/factories', __FILE__) if defined?(FactoryBot)
    end
  end
end

Step 5[Optional but Recommended]: Configure your test suite to include factory_bot methods

  • One can avoid explicitly specifying FactoryBot.method_name in their specs whenever they want to use a Factory Bot method by adding the below changes based on the configuration section that has been linked to as part of the factory_bot_rails README.

    • We need to first add the below code in a file called factory_bot.rb located within spec/support
      RSpec.configure do |config|
        config.include FactoryBot::Syntax::Methods
      end
    
    • We then need to add require 'support/factory_bot' in our rails_helper.rb since the support folder isn't eagerly loaded

Side note: The changes as part of steps 1-4 are available in this PR and the step 5 changes can be seen in this PR

Setting up Spring

Step 1: Add the necessary gems to the Gemfile

  • To setup spring with RSpec we need to firstly add spring and spring-commands-rspec gems to the engine's gemfile. These gems are added to one's Gemfile instead of having them specified in the gemspec file as they are neither a development dependency nor a runtime dependency for an engine to work.

Step 2: Allow Spring to know where the engine app is located

  • Next, since we're using Spring in the context of an engine, we need to explicitly tell Spring where our app is located in the context of an engine. We can do that by specifying the below command in a newly created file called spring.rb within engine_name/config directory. The below command was also specified in the official Spring repo README, but initially, it wasn't very clear(atleast to me) in which file this code needs to be added. After finding out on where exactly this code needs to be added via this github issue, I've submitted a PR as an attempt to make this clearer as part of Springs README.
  Spring.application_root = './spec/dummy'

Step 3: Springify executables

  • Now that we've told Spring where our app is located, we can follow this up with running bundle exec spring binstub --all to springify the executables in one's bin/ directory. You should now be able to run a command like spring rspec spec/models/sample_engine/widget_spec.rb from your engine's root directory.

Side note: The above steps are followed in this PR if you'd like to see them in more detail in terms of code.

How did your experience of trying the above steps go? If you have any feedback or thoughts to share wrt this post, I look forward to hearing from you via the comments box below.

Discover and read more posts from Mohnish Jadwani
get started
post commentsBe the first to share your opinion
Show more replies