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-rails
gem - Generate a
User
model - Write a single test for that
User
model
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
User
object with first nameAbraham
and last nameLincoln
. - Verify that when we call this
User
object’sfull_name
method, it returns the full name,Abraham Lincoln
.
What are the it
s and describe
s 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