This is a base line guide to setup a solid rails project in no time. I’m going to install rails, rspec and factory bot. I will use only the latest and the greatest versions on this guide. Lets do this.
Install Rails
$ gem install rails --pre
$ rails _6.0.0.beta3_ new test_app -T -d postgresql
The first line will install rails 6 beta and the second will create a rails application, the options are for:
- -T: skips default rails testing framework, I’m going to use Rspec.
- -d postgresql: informs rails to use postgres as database.
Install Rspec
Change the Gemfile, adding the gems on the group development and test, like this:
#Gemfile
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'rspec-rails', '~> 3.8'
gem 'factory_bot_rails'
gem 'capybara'
end
Then run in the terminal:
$ bundle install
$ bin/rails db:create db:migrate
$ bin/rails g rspec:install
The output should look like:
To complete the installation, we have to inform rspec to use capybara so add this line on spec_helper:
#spec/spec_helper.rb
require 'capybara/rspec'
Also, I want configure rspec-rails to use chrome headless so we test javascript too.
$ mkdir spec/support
$ touch spec/support/capybara.rb
#spec/support/capybara.rb
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :rack_test
end
config.before(:each, type: :system, js: true) do
driven_by :selenium_chrome_headless
end
end
on spec/rails_helper.rb uncomment that line:
#spec/rails_helper.rb
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }
Convenience configurations(optional)
This configurations add some convenience to our work-flow, feel free to change anything.
Better Rspec Output
add this line to .rspec file to get a more readable output:
--format documentation
Configure Generators
I’m basically disabling some tests we don’t need by default so we can create by hand if it is appropriate.
#config/application.rb
config.generators do |g|
g.test_framework :rspec,
fixtures: false,
view_specs: false,
helper_specs: false,
routing_specs: false
end
FactoryBot Helpers
Add this line on rails_helper.rb
#spec/rails_helper.rb
config.include FactoryBot::Syntax::Methods
Generate Binstubs
Finally run this command to generate binstubs for rspec so can run our specs with bin/rspec instead of the verbose bundle exec rspec:
$ bundle binstubs rspec-core
Smoke Test
Lets see if everything works as expected. At moment as I write this rspec-rails hasn’t a generator for system specs on the latest release. So I will do it by hand.
$ mkdir spec/system
$ touch spec/system/hello_system_spec.rb
#/spec/system/hello_system_spec.rb
require "rails_helper"
RSpec.describe "Hello", type: :system do
it 'it says hello' do
visit "/hello/index"
expect(page).to have_text("Hello#index")
end
end
$ bin/rails g controller Hello index
To run the tests:
$ bundle exec rspec spec/system
if everything is fine you should see this screen on your terminal:
FactoryBot
FactoryBot is already installed, but is good to test if everything works as expected, to do this lets create a simple model:
rails g model Post title:string content:text
rails db:migrate
Our simple model spec should look like:
require 'rails_helper'
RSpec.describe Post, type: :model do
it "has a valid factory" do
expect(FactoryBot.build(:post)).to be_valid
end
end
If its working we should see this:
Great factorybot is working.
Shoulda Matchers
As the final step lets install shoulda matchers
Add this to Gemfile
#Gemfile
group :test do
gem 'shoulda-matchers'
end
On terminal run:
$ bundle install
Add on spec/rails_helper.rb
# spec/rails_helper.rb
Shoulda::Matchers.configure do |config|
config.integrate do |with|
with.test_framework :rspec
with.library :rails
end
end
class ActiveModel::SecurePassword::InstanceMethodsOnActivation; end;
The last line here is a temporary fix to an issue with rails 6 for more details see
alter spec/models/post_spec.rb to make it look like this:
#spec/models/post_spec.rb
require 'rails_helper'
RSpec.describe Post, type: :model do
it "has a valid factory" do
expect(build(:post)).to be_valid
end
it { should validate_presence_of(:title) }
end
This test will fail unless we add this validation on post model:
#app/models/post.rb
class Post < ApplicationRecord
validates :title, presence: true
end
If we run rspec again this should be the output:
Thats conclude our setup. As a last step I will remove the post model used for testing the setup with:
$ rails destroy model Post
On part 2 we going to lint code with Rubocop and use SimpleCov to gather code coverage.
additional links: