Integration testing for Rails APIs part 3: Automating tedious things
In this final part of the series we’ll take a look at the tests we’ve written. Looks like there’s some duplicated code; learn how to eliminate it using automated JSON validation and RSpec’s shared examples.
Validating JSON responses
Manually parsing and checking JSON using
JSON.parse is a pain: you need to be aware of the structure in each spec.
Wouldn’t it be nice if we could define the structure of an object in a single file and then validate responses using that file? With JSON schema we can!
JSON schema is a format for describing JSON documents structurally. The latest draft is version 4, which I’ll be using in these examples. Even though the spec is still in draft form, v4 is full-featured and good for most use cases.
Describing a user resource with JSON schema
Now, don’t start thinking of XML here: JSON schema is actually easy to read and write unlike XSD.
We’ll use the
GET /users/:id endpoint from the previous part as an example. The returned user resource has three attributes and looks like this:
The role attribute is only shown if the current user has the
Let’s think about the data types for a second. Our resource document’s root is an object with the following properties:
- string, looks like an email address
- string, either “user” or “admin”
The schema for the user resource would then look like this:
We’ll store our schemas in the
/spec/support/schemas folder so that we can easily use them for testing.
As you can see, the schema is a plain ol’ JSON file. The
$schema property lets the validator know which version of JSON schema to use. It’s optional: in case it’s missing, the validator will use the most recent version it supports.
Since we’re in the root element of our schema, the
type attribute defines the root element of the described document. The root element of our user resource is an object.
An object’s properties can be described with the same syntax as all elements. They also have a
type property, we’re using the
enum notation is special: it defines an array of values that the property can have. It’s not limited to strings, you can specify all kinds of values, including
Required properties are listed in
required. Note that extra properties that haven’t been defined in the schema are allowed by default: to disallow them, set
The schema format allows for advanced features such as referencing other schemas and defining your own data types.
Writing a schema matcher
Using the json-schema gem and a custom RSpec matcher, we can now validate responses against that schema.
Below is the matcher that I use.
Note that you don’t have to copy it into your project. I’ve packaged it into a gem called rspec_json_schema_matcher that you can install.
Assuming that you have a schema called
/spec/support/schemas, you can validate a response body against it like this:
What JSON schema doesn’t do
Your API might be returning valid data, but does it make sense? Remember, the schema validates only the structure, not the content.
When testing your API, you should have some tests in place to ensure that the data actually makes sense:
- Are you returning the right record?
- Does the record change when you update it?
To help test these, it’s useful to have some utilities installed for working with JSON. I’ve found the json_spec gem to be good for most of my use cases.
Shared examples for less duplication
If you output helpful JSON summaries of errors, you’ll soon start to notice that you’re writing the same code again and again when testing for them.
RSpec lets you write shared examples to eliminate duplicated code. I’ve found them to be useful when checking for responses that are pretty much the same everywhere. Errors are prime candidates here.
If you’ve written schemas for your error summaries, validating them becomes suddenly quite easy:
Usually I have a bunch of shared examples under
/spec/support/shared_examples that I require in
Now testing an error response becomes a one-liner in the spec:
This concludes my series on API testing tips. I hope you’ve learned something useful that you can use in your projects!
Let me know in the comments below if you have any questions or feedback.
Even though this series ends here, I’ve got all sorts of goodies lined up. Don’t forget to subscribe to the newsletter below so that you’ll be the first one to know about them!