The following is an excerpt from my book, Rails Testing for Beginners.
What we’re going to do
What we’re going to do in this post is:
- Initialize a new Rails application
- Install RSpec using the
rspec-railsgem - Generate a
Usermodel - Write a single test for that
Usermodel
The test we’ll write will be a trivial one. The user will have both a first and last name. On the User model we’ll define a full_name method that concatenates first and last name. Our test will verify that this method works properly. Let’s now begin the first step, initializing the Rails application.
Initializing the Rails application
Let’s initialize the application using the -T flag meaning “no test framework”. (We want RSpec in this case, not the Rails default of MiniTest.)
$ rails new model_test_hello_world -T $ cd model_test_hello_world
Now we can install RSpec.
Installing RSpec
To install RSpec, all we have to do is add rspec-rails to our Gemfile and then do a bundle install.
# Gemfile group :development, :test do gem 'rspec-rails' end
$ bundle install
We’ve installed the RSpec gem but we still have to run rails g rspec:install which will create a couple configuration files for us.
$ rails g rspec:install
Now we can move on to creating the User model.
Creating the User model
Let’s create a User model with just two attributes: first_name and last_name.
$ rails g scaffold user first_name:string last_name:string $ rails db:migrate
Now we can write our test.
The User test
Writing the test
Here’s the test code:
# spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe '#full_name' do
it 'concatenates first and last name' do
user = User.new(first_name: 'Abraham', last_name: 'Lincoln')
expect(user.full_name).to eq('Abraham Lincoln')
end
end
end
Let’s take a closer look at this test code.
Examining the test
Let’s focus for a moment on this part of the test code:
it 'concatenates first and last name' do
user = User.new(first_name: 'Abraham', last_name: 'Lincoln')
expect(user.full_name).to eq('Abraham Lincoln')
end
This code is saying:
- Instantiate a new
Userobject with first nameAbrahamand last nameLincoln. - Verify that when we call this
Userobject’sfull_namemethod, it returns the full name,Abraham Lincoln.
What are the its and describes all about? Basically, these “it blocks” and “describe blocks” are there to allows us to put arbitrary labels on chunks of code to make the test more human-readable. (This is not precisely true but it’s true enough for our purposes for now.) As an illustration, I’ll drastically change the structure of the test:
# spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
it 'does stuff' do
user = User.new(first_name: 'Abraham', last_name: 'Lincoln')
expect(user.full_name).to eq('Abraham Lincoln')
end
end
This test will pass just fine. Every it block needs to be nested inside a do block, but RSpec doesn’t care how deeply nested our test cases are or whether the descriptions we put on our tests (e.g. the vague and mysterious it 'does stuff') make any sense.
Running the test
If we run this test it will fail. It fails because the full_name method is not yet defined.
F
Failures:
1) User#full_name concatenates first and last name
Failure/Error: expect(user.full_name).to eq('Abraham Lincoln')
NoMethodError:
undefined method `full_name' for #<User:0x00007ff73d419e20>
# ./spec/models/user_spec.rb:7:in `block (3 levels) in <top (required)>'
Finished in 0.00695 seconds (files took 0.77932 seconds to load)
1 example, 1 failure
Failed examples:
rspec ./spec/models/user_spec.rb:5 # User#full_name concatenates first and last name
Let’s now write the full_name method.
# app/models/user.rb
class User < ApplicationRecord
def full_name
"#{first_name} #{last_name}"
end
end
Now the test passes.
. Finished in 0.008 seconds (files took 0.80982 seconds to load) 1 example, 0 failures