Integration testing for Rails APIs part 1: Structuring specs
Writing an API with Rails? Learn how to avoid making a mess when testing. Structure your integration specs in a logical manner to make maintaining them a lot easier.
Lately I’ve been working on a few Rails-backed APIs. Most of them are based on the excellent rails-api gem that puts together a lighter version of Rails specifically with APIs in mind.
To build a stable API, I need tests to make sure that any changes won’t suddenly break compatibility. There’s no way I’m building an API without tests: they help me refactor with confidence during the day and sleep soundly at night!
A three-part series
This was supposed to be a single post about how I do integration testing, but apparently I had way too much to say. Let’s call this part one then, shall we?
This series is divided into three parts:
- Structuring specs
- Writing less code
- Automating tedious things
I’m writing on an intermediate level here; I assume that you have a pretty good grasp on BDD jargon and you’ve done testing with Rails before.
First let’s lay the groundwork for the next two parts. In this part I’ll show you how to organise and write your specs in a more manageable way.
Let’s get started!
It’s easy to make a mess
The thing that makes effective integration testing difficult is that it’s easy to make a mess with duplicated code and lengthy examples.
Working with an API just makes this issue worse: the output is well defined, so it’s tempting to just throw lots of expectations together (maybe copy some from another spec) and call it a day.
You’ll start to notice this problem when your specs are hundreds of lines long and you have trouble navigating through the sea of duplicated code.
One endpoint, one method, one file
Shorter files are easier to manage. Describing an API endpoint with all of its calls in a single file is usually too lengthy, but one API call per file has worked well for me.
Let’s imagine that you have a
/users endpoint with these calls:
I usually name my specs according to their controller actions so that the specs are named uniquely within their subfolder.
The endpoint above would have these specs:
And I’d describe the HTTP method and path with parameters, if any:
context to structure specs logically
Remember when we talked about making a mess? Luckily RSpec has a bunch of features that help you reduce duplicated code.
context are my favourite ones since they work so well together.
let method defines a helper method that’s lazily evaluated when used for the first time in an example. It’s not shared across examples, which makes it ideal for defining “variables” at the top of your tests.
context method defines a nested group of examples. Like you’d expect, it’s used for adding context to each of its sub-examples, e.g. “when the user exists, these examples should pass.”
When used together, these two become your BDD besties. You can use
let to establish a described context:
But it gets even better! Since helpers defined with
let are not shared across examples, you can redefine them within an example group.
The logical next step is to define defaults for the common case at the top of the spec and then override them based on the context. You can even reference other helpers:
With a structure like this, you can easily pinpoint what’s being changed within a given context. There’s no need to duplicate any of the default values.
Note that the default case (the one not overriding any of the defaults) is described first. That’s a good habit to get into for readability.
To reiterate: use
let to set up the default (or positive) case at the top of the file. Then change one thing in each subsequent
context by overriding the defaults.
before is where the request happens
With rspec-rails you get a bunch of helpers like
post that help you make those requests. After calling one of them, you have
response at your disposal.
Don’t stick the request right inside the example. We’re describing a single API call per file, so we’re basically testing the same request with different parameters, right?
context, you never have to repeat the request as long as you declare the variables (if any) and stick the request in a
You can then manipulate just the bits you need:
These are just a few techniques that help you organise your specs. Check out betterspecs.org for more RSpec best practices.
My next post in this series will be about things that help you write less testing code. I’ll be sending out a newsletter when it’s ready so make sure to subscribe below if you’re interested.
As always, leave a comment or tweet at me if you have any questions, comments or criticism! See you again in the next post.