Built on (R)Spec: Part 1

Getting started with testing your Rails API models

Sarah Laloggia
The Startup
5 min readApr 3, 2020

--

Having recently graduated from a coding bootcamp, and talking to others who started in similar programs, I’ve noticed something pretty consistent: most of these programs talk quite a bit about the value of Test-Driven Development, and may even teach their students how to engage with the process using exercises with pre-written tests…but they rarely spend much time helping students learn how to setup a testing suite or write their own tests.

I decided to try to bridge this gap by writing some tests for my completed projects — code that I already know works. Since my bootcamp had us do a lot of work in Ruby on Rails, I was somewhat familiar with RSpec and this seemed like a good place to start. So let’s walk through setting up RSpec and testing a simple API in Rails.

Setting up

If you are adding RSpec to an existing project, and you didn’t use the “-T” tag when creating your Rails app, there should be a “test” directory included automatically. You can delete this before installing RSpec, to avoid confusion.

Install RSpec

In your Gemfile, add rspec to the “:development, :test” group…

…and run

This will install the gem and generate the spec folder containing the required helper files that rspec needs.

Configure the Database Cleaner

Database Cleaner is a tool that cleans up your test database as you use it, so that data from previous tests doesn’t persist and mess up future results.

Recent versions of RSpec include and configure Database Cleaner automatically, but API-only builds in Rails have some quirks that make additional configuration necessary.

Make sure your rails_helper file includes the following:

Set up spec files for Models and Controllers

When you run rspec, it looks for files using specific naming conventions, for example “spec/models/modelname_spec.rb.

You can create the folder/file structure manually, but Rails has a generator that will make it easy to ensure that everything is named correctly:

Basic test construction

Tests in RSpec are comprised of three basic components: a describe block, an it block, and one or more expectation expressions.

As with anything in coding, there are many ways to refactor this structure to maximize flexibility and efficiency, including optional components like before and context blocks that you may have seen before. In the interests of clarity and predictability while we’re learning, I’m going to stick to the format above.

Testing your models

I find it helpful to start with testing the most basic Model functionality: creating a new instance and saving it to the database.

Here, we’re going to walk through setting up those tests for a simple User model, with a “name” attribute.

  • First we lay out the file with the default RSpec describe block, and a describe block for “Creating a new User”
  • Next we will need to declare some variables that will be called on within our tests. Since we want to test that our Users are being saved to the database, we’ll have one variable be the count of all Users, before we create one for the test. Then we’ll declare a variable to represent our newly-created User.

SOME NOTES ABOUT VARIABLE DECLARATION:

  1. Variables can only be declared in describe (and later, before and context) blocks. Not in the it statements that comprise the individual tests.
  2. When declaring variables in a describe block, as we will be doing, you should use either let or let!. The first creates a variable that is evaluated lazily — that is, it doesn’t exist until it is called within a test. The bang operator causes your variable to evaluate immediately, before any tests are run. I am using let! because I find it easier to understand and predict how the variables behave. However, this does make for less-efficient, slower tests.
  3. As you get more comfortable with RSpec, you will want to learn about before blocks and how to use them to declare variables more efficiently.

Now we can write our first tests, using RSpec’s built-in .to method and a couple of it’s built-in matchers.

For the first test, we just want to make sure our user object is in fact an instance of the User class:

Next we’ll make sure we can access the object’s attributes, in this case “name”:

And then we’ll confirm that our user was saved to the database. This is where our starting_db_count variable comes in — since it was set with the count before we created our user, the count should have increased by 1 once user was saved to the database:

So at this point, our user_spec file, with our first group of tests looks like this:

Once those tests are passing, we’re going to add a group for testing our model’s validations!

We’re going to keep working with our simple User class, with its single ‘name’ attribute. Let’s assume that our model validates that a User’s name is at least 5 characters long:

  • We need to add a new describe block for our Validation grouping, and we’ll go ahead and we’ll start with a test confirming that a User is valid if the attributes meet the standards of it’s validations:
  • You may have noticed that we used another of RSpec’s built-in matchers: be_valid
  • This test should already be passing, since the user we create at the top has a name with 8 characters.

Our final test in this grouping is going to confirm that a new User instance with a name of less than five characters is declared invalid.

  • For this, we are going to use a variation of the .to method that we have been using so far: .to_not
  • We also need to change the ‘name’ attribute on our ‘user’ variable. While you cannot declare variables inside a test, you can make changes to them.

Now we have a basic test file for our User model!

Obviously most of the models you work with irl will be more complex, but I hope this exercise helps you feel more comfortable with the process of writing tests. I encourage you to go through some of your older Rails projects and write tests for all of your models, covering all their attributes and validations.

As you feel more comfortable, check out BetterSpecs for advice on best practices and efficiency, and the Shoulda-Matchers gem for expanding your library of intuitive matchers.

Complete user_spec file

In my next post, we’ll walk through another important step in API testing: ROUTES!

Coming Soon: Part 2 — Testing your routes

Further Reading:

--

--