Let’s find out how to configure a best test suite in Rails by using Rspec framwork and the other libraries that support rspec for writing smooth tests. We will be removing rails native test folder and other configurations.
For that we will be using Rspec, Factory bot for factories, Database cleaner.
Lets start from adding these gems into our Gemfile
group :development, :test do
# Rspec testing module and needed libs
gem 'factory_bot_rails', '5.2.0'
gem 'rspec-rails', '~> 4.0.0'
end
group :test do
# db cleaner for test suite
gem 'database_cleaner-active_record', '~> 2.0.1'
end
Now do
bunde install # this installs all the above gems
If you have already built the Rails app, your app may contain Rails own test suite. Remove the native rails test suite to add Rspec module to your project.
We use Rspec over rails native test module because rspec provides better helpers and machnism than Rails native test.
in application.rb
file comment the following line
# require 'rails/test_unit/railtie'
inside the class Application
add this line:
# Don't generate system test files.
config.generators.system_tests = nil
Remove the native rails test folder:
rm -r test/
We use factories over fixtures. Remove this line from rails_helper.rb
config.fixture_path = "#{::Rails.root}/spec/fixtures"
and modify this line to:
config.use_transactional_fixtures = false # instead of true
This is for preventing rails to generate the native test files when we run rails generators.
Database Cleaner
Now we configure the database cleaner that is used for managing data in our test cycles.
Open rails_helper.rb
file and require that module
require 'rspec/rails'
require 'database_cleaner' # <= add here
Note: Use only if you run integration tests with capybara
or dealing with javascript codes in the test suite.
“Capybara spins up an instance of our Rails app that can’t see our test data transaction so even tho we’ve created a user in our tests, signing in will fail because to the Capybara run instance of our app, there are no users.”
I experienced database credentials issues:
➜ rspec
An error occurred while loading ./spec/models/user_spec.rb.
Failure/Error: ActiveRecord::Migration.maintain_test_schema!
Mysql2::Error::ConnectionError:
Access denied for user 'username'@'localhost' (using password: NO)
First I thought of using database cleaner, later I realised that this error is because of my credentials.yml.enc
file was corrupted somehow. I don’t know how that happend. So try to edit and see your credentials still exists in this file.
EDITOR="code --wait" bin/rails credentials:edit
Now in the Rspec configuration block we do the Database Cleaner
configuration.
Add the following file:
spec/support/database_cleaner.rb
Inside, add the following:
# DB cleaner using database cleaner library
RSpec.configure do |config|
# This says that before the entire test suite runs, clear
# the test database out completely
config.before(:suite) do
DatabaseCleaner.strategy = :transaction
DatabaseCleaner.clean_with(:truncation)
end
# This sets the default database cleaning strategy to
# be transactions
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
# include this if you uses capybara integration tests
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
# These lines hook up database_cleaner around the beginning
# and end of each test, telling it to execute whatever
# cleanup strategy we selected
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
and be sure to require this file in rails_helper.rb
require 'rspec/rails'
require 'database_cleaner'
require_relative 'support/database_cleaner' # <= here
Configure Factories
Note: We use factories over fixtures because factories provide better features that make writing test cases an easy task.
Create a folder to generate the factories:
mkdir spec/factories
Rails generators will automatically generate factory files for models inside this folder.
A generator for model automatically creating the following files:
spec/models/model_spec.rb
spec/factories/model.rb
Now lets load Factory bot configuration to rails test suite.
Add the following file:
spec/support/factory_bot.rb
and be sure to require this file in rails_helper.rb
require 'rspec/rails'
require 'database_cleaner'
require_relative 'support/database_cleaner'
require_relative 'support/factory_bot' # <= here
You can see the following line commented
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
You can uncomment it to make all the factories available in your test suite. But I don’t recommend that to load all the files. We will be loading each factory whenver it is necessary.
Final rails_helper.rb
file. We won’t use capybara for integration tests. So we are not adding database_cleaner
configuration.
# This file is copied to spec/ when you run 'rails generate rspec:install'
require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test'
require File.expand_path('../config/environment', __dir__)
# 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_relative 'support/factory_bot'
# Checks for pending migrations and applies them before tests are run.
# If you are not using ActiveRecord, you can remove these lines.
begin
ActiveRecord::Migration.maintain_test_schema!
rescue ActiveRecord::PendingMigrationError => e
puts e.to_s.strip
exit 1
end
RSpec.configure do |config|
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
config.infer_spec_type_from_file_location!
# Filter lines from Rails gems in backtraces.
config.filter_rails_from_backtrace!
# arbitrary gems may also be filtered via:
# config.filter_gems_from_backtrace("gem name")
end
A spec directory look something like this:
spec/
controllers/
user_controller_spec.rb
product_controller_spec.rb
factories/
user.rb
product.rb
models/
user_spec.rb
product_spec.rb
mailers/
mailer_spec.rb
services/
service_spec.rb
rails_helper.rb
spec_helper.rb
References:
https://github.com/rspec/rspec-rails
https://relishapp.com/rspec/rspec-rails/docs
https://github.com/thoughtbot/factory_bot/blob/master/GETTING_STARTED.md#configure-your-test-suite
https://github.com/DatabaseCleaner/database_cleaner
Model Specs
Lets generate a model spec. A model spec is used to test smaller parts of the system, such as classes or methods.
# RSpec also provides its own spec file generators
➜ rails generate rspec:model user
create spec/models/user_spec.rb
invoke factory_bot
create spec/factories/users.rb
Now run the rpsec command. That’s it. You can see the output from rspec.
➜ rspec
*
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Item add some examples to (or delete) /home/.../spec/models/user_spec.rb
# Not yet implemented
# ./spec/models/user_spec.rb:4
Finished in 0.00455 seconds (files took 1.06 seconds to load)
1 example, 0 failures, 1 pending
Lets discuss how to write a perfect model spec in the next lesson.