RSpec is a Rails gem designed to make it easy to write unit and feature tests. If you run into any trouble, you can read the docs here
Throughout my career and especially while job-hunting, the number one piece of advice I was given was to write unit tests.
In any given situation, be it a take-home exam or a portfolio piece, if you write tests it will show that you're a thoughtful engineer who takes time to think through problems.
And your code will be better for it.
In this article we'll talk through how you can get RSpec running, the basics of writing your first unit tests, and some nifty features in RSpec to help keep your tests clean and organized.
Let's dive in:
Installing RSpec
Before you can start writing tests with RSpec, you'll need to install the gem and set up your Rails application. To get started, follow these steps:
- Open your
Gemfile and add the rspec-rails gem to the development and test group:
group :development, :test do
gem 'rspec-rails', '~> 5.0'
end
Install the gem by running bundle install
Run the RSpec generator with rails generate rspec:install. This command creates a .rspec file, a spec folder, and a spec_helper.rb file in your project directory.
After this is configured, you're ready to write your first unit test!
Writing our first tests
With RSpec installed, let's create a simple model and write a unit test to ensure it's working correctly.
For this example, we'll create a User model with a name attribute. Run rails generate model User name:string to generate the necessary files. You'll have to run a migration for this change to be added to your database.
Add the following code to our model to validate the presence of a name field:
class User < ApplicationRecord
validates :name, presence: true
end
Note that in the spec folder, a new folder called models, and inside it, a file named user_spec.rb has been generated. This is where we'll write our tests for the User model.
Open user_spec.rb and start by creating a new describe block for the User model and write a simple test to check if a user with a valid name can be created
RSpec.describe User do
it 'is valid with a name' do
user = User.new(name: 'John Doe')
expect(user).to be_valid
end
it 'is not valid without a name' do
user = User.new
expect(user).to_not be_valid
end
end
- To execute your test, run
bundle exec rspec in your terminal. If everything is set up correctly, you should see an output indicating that the test passed.
You can now write additional tests for your User model or any other part of your application by following the same pattern within the spec folder.
While none of these are necessary to function, RSpec does have a whole host of cool features that help promote clean and organized unit tests.
It's easy for these files to get long and unwieldy, so keeping your codebase clean and DRY will demonstrate expertise.
Below, I'll detail each of RSpec's interesting features that we use regularly in our day-to-day development work, as well as a code example to demonstrate what I mean.
Context blocks
The context method helps create logical groups for your tests based on specific conditions or scenarios.
This can make your tests more readable and better organized.
RSpec.describe User do
context 'when name is present' do
let(:user) { User.new(name: 'John Doe') }
it 'is valid' do
expect(user).to be_valid
end
end
context 'when name is not present' do
let(:user) { User.new }
it 'is not valid' do
expect(user).to_not be_valid
end
end
end
Shared examples
RSpec allows you to define reusable examples that can be included in multiple test groups, helping you keep your tests DRY.
shared_examples 'a valid user' do
it 'is valid' do
expect(user).to be_valid
end
end
describe AdminUser do
let(:user) { AdminUser.new(name: 'John Doe') }
it_behaves_like 'a valid user'
end
describe RegularUser do
let(:user) { RegularUser.new(name: 'Jane Doe') }
it_behaves_like 'a valid user'
end
Custom matchers
RSpec allows you to create custom matchers that make your tests more expressive and readable.
RSpec::Matchers.define :be_a_valid_user do
match do |user|
user.valid?
end
end
describe User do
let(:user) { User.new(name: 'John Doe') }
it 'is valid with a name' do
expect(user).to be_a_valid_user
end
end
Final thoughts
As someone who's been involved both in interviewing and in hiring, I've seen that one of the top ways a candidate can stay out is by writing a suite of thoughtful unit tests.
It's an immediate signal to the hiring team that you're a professional and you know what you're doing.
Use this post as a reference and write some tests!
Thanks for reading
Austin