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 ofspec
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 commands.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 atlib/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 thespec/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 yourspec/rails_helper.rb
withrequire 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 aRAILS_ENV=test rake db:migrate
wrt your engine), they are added to theschema.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 thespec/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 locationtest/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 yourrails_helper.rb
else you will get anuninitialized constant
related error when you try using a command likeFactoryBot.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 thefactory_bot_rails
repo. You're factories would be created by a standalone engine without the below code and thereby including the below code in yourengine.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 withinspec/support
RSpec.configure do |config| config.include FactoryBot::Syntax::Methods end
- We then need to add
require 'support/factory_bot'
in ourrails_helper.rb
since the support folder isn't eagerly loaded
- We need to first add the below code in a file called
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
withinengine_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'sbin/
directory. You should now be able to run a command likespring 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.