A Rails model test “hello world”

by Jason Swett,

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:

  1. Initialize a new Rails application
  2. Install RSpec using the rspec-rails gem
  3. Generate a User model
  4. 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:

  1. Instantiate a new User object with first name Abraham and last name Lincoln.
  2. Verify that when we call this User object’s full_name method, 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

Leave a Reply

Your email address will not be published. Required fields are marked *