A Ruby on Rails Engine does not come with Rspec. The setup is simple which you can find below along with instructions for adding Factory Bot.
Generate an engine
Lets generate a new engine. We typically like to have an engines folder so we will generate our new engine there.
rails plugin new engines/my_feature --mountable --skip-test --dummy_path=spec/dummy
Include the --skip-tests
option because we plan on using Rspec instead of MiniTest. We also are specifying the dummy path to create a dummy application for our specs to use.
Add and Configure Rspec
Next step is to add Rspec. Navigate to your engine’s gemspec file. In our case it is located at engines/my_feature/my_feature.gemspec
. Complete all of the TODO’s.
Add our development dependency for rspec.
engines/my_feature/my_feature.gemspec
spec.add_development_dependency "rspec-rails"
At this point, you should be able to bundle install from your engine.
root/engines/my_feature $ bundle install
Install rspec
root/engines/my_feature $ rails g rspec:install
create .rspec
exist spec
create spec/spec_helper.rb
create spec/rails_helper.rb
If you try to run rspec now, you will likely run into a Load Error. This is because the rails_helper
is trying to require an environment file from the engines root. Our environment file is located within our dummy app.
Modify engines/my_feature/spec/rails_helper.rb
# require File.expand_path('../../config/environment', __FILE__)
require File.expand_path('../dummy/config/environment', __FILE__)
You can confirm rspec is installed by running it
$ rspec spec/
Finished in 0.00037 seconds (files took 0.05945 seconds to load)
0 examples, 0 failures
Now it is time to add some tests. Let’s generate a User
model so we can test.
root/engines/my_feature $ rails g model User
invoke active_record
create db/migrate/20190406181424_create_my_feature_users.rb
create app/models/my_feature/user.rb
Notice that a spec file for User
was not generated. To get that setup we will configure our generators in our engine.rb
.
Destroy the user model
root/engines/my_feature $ rails d model User
invoke active_record
remove db/migrate/20190406181424_create_my_feature_users.rb
remove app/models/my_feature/user.rb
Update /engines/my_feature/lib/my_feature/engine.rb
module MyFeature
class Engine < ::Rails::Engine
isolate_namespace MyFeature
config.generators do |g|
g.test_framework :rspec
end
end
end
Regenerate our User
which should include a spec
root/engines/my_feature $ rails g model User
invoke active_record
create db/migrate/20190406182129_create_my_feature_users.rb
create app/models/my_feature/user.rb
invoke rspec
create spec/models/my_feature/user_spec.rb
Depending on how you manage your migrations, you may also need to set the migration path to be scoped to our dummy application.
engines/my_feature/spec/rails_helper.rb
ENGINE_ROOT = File.join(File.dirname(__FILE__), '../')
begin
ActiveRecord::Migrator.migrations_paths = File.join(ENGINE_ROOT, 'spec/dummy/db/migrate')
ActiveRecord::Migration.maintain_test_schema!
...
Create and migrate your test database if you have not already already
root/engines/my_feature $ rails db:create db:migrate RAILS_ENV=test
Run rspec
root/engines/my_feature $ rspec spec/
Finished in 0.0061 seconds (files took 2.6 seconds to load)
1 example, 0 failures, 1 pending
Hopefully you see one pending test for our User spec.
Add Factory Bot
Last step, we are going to add factory girl. We will need to make a few changes to get this working. First, add another development dependency to our gemspec.
spec.add_development_dependency "factory_bot_rails"
root/engines/my_feature $ bundle install
Revisit our configuration change to our engine.rb
. Modify the block to include factory_bot.
config.generators do |g|
g.test_framework :rspec
g.fixture_replacement :factory_bot
g.factory_bot dir: 'spec/factories'
end
We need to ensure our factories will be loaded when we run our test suite. Open the rails helper and add the following:
engines/my_feature/spec/rails_helper.rb
require 'factory_bot_rails'
FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
FactoryBot.factories.clear
FactoryBot.find_definitions
Rspec also needs to include factory bot methods so we can do things like create(:user)
and build(:user)
in our tests. That can be done by including the methods in our Rspec configure block.
engines/my_feature/spec/rails_helper.rb
RSpec.configure do |config|
...
config.include FactoryBot::Syntax::Methods
...
Next time you generate a model, you should also have a new factory.
root/engines/my_feature $ rails g model Car
invoke active_record
create db/migrate/20190406184550_create_my_feature_cars.rb
create app/models/my_feature/car.rb
invoke rspec
create spec/models/my_feature/car_spec.rb
invoke factory_bot
create spec/factories/my_feature/cars.rb
root/engines/my_feature $ rails db:drop db:create db:migrate RAILS_ENV=test
Our Car
factory is generated in spec/factories/my_feature/cars.rb
.
The factory bot generator might not have name-spaced the class so be sure to check.
factory :my_feature_car, class: 'MyFeature::Car'
Open the car spec and add a test.
engines/my_feature/spec/models/car_spec.rb
it 'should create a car' do
car = create(:my_feature_car)
expect(MyFeature::Car.count).to eq(1)
end
Run rspec and we should see a passing car spec.
root/engines/my_feature $ rspec spec/
Finished in 0.01697 seconds (files took 3.78 seconds to load)
2 examples, 0 failures, 1 pending
Takeaway
I hope this helped you get rspec setup in your rails engine. As you create more engines, you might start to find these steps repetitive. In a future post, we will explore different ways to reduce the need of the boilerplate configuration.
Found this useful? Have a suggestion? Get in touch at blog@hocnest.com
.