Testing your Rails APIs with Airborne.

Timi Ajiboye
chunks of code*
Published in
4 min readJan 29, 2017

It took me a long long time to finally decided to write tests. I’ll probably write about the thought process and why I think you should write tests later.

If you use Rails to build REST APIs, and you want the number of times the FE Engineer you’re working with complains that an endpoint isn’t working, this post is for you.

Airborne is an RSpec driven API testing framework that let’s you write tests for APIs built with Ruby in a very intuitive easy to grok way. Unfortunately, it’s documentation on working with Rails APIs isn’t as thorough as I would have liked.

So in this post, I’m going to touch on all the different aspects of using Airborne with Rails that weren’t clear at first to me. Besides the things I’m explaining here, everything else about using Airborne is well described in it’s documentation.

(Some experience with RSpec would make this easier to read/understand)

Installation

You have to install RSpec as Airborne is dependent on it.

group :development, :test do
gem 'rspec-rails', '~> 3.5'
end
group :test do
gem 'airborne'
end

Add the above to your Gemfile and run bundle install.

Then run rails generate rspec:install.

Once installed, RSpec will generate spec files instead of Test::Unit test files when commands like rails generate model and rails generate controller are used.

How to use

Introduction

Generally, in Rails REST API applications (that don’t use tools like Grape), your endpoints correspond to a controller action. Since Airborne is RSpec driven, it makes sense that your endpoint tests will live in the spec for your controller.

For example, if you have a tickets_controller.rb that handles all endpoints related to getting a list of tickets, creating tickets and editing tickets. Your API tests will live in /spec/controllers/tickets_controller_spec.rb. As stated above RSpec will generate the appropriate spec files if you use the rails generate controller command.

Making Requests

require 'rails_helper'

RSpec.describe TicketsController, :type => :controller do
describe 'GET index' do
it 'returns correct types' do
get :index
expect_json_types(foo: :string)
end
end
end

The above shows a simple test in tickets_controller_spec.rb. This will make a GET request to the index action in the TicketsController and check if the JSON response contains a value foo that’s a string.

Pretty straightforward right?

You can do the same for post, put, patch, and delete requests.

Query Parameters, Request Body and Headers

The Airborne documentation doesn’t detail how to pass query parameters or a request body when testing a Rails Controller. All you have to do however is pass everything as a hash as the second argument.

describe 'POST create' do
it 'returns correct types' do
body = {'subject' => 'Hey', 'body' => 'Testbody'}
post :create, body
expect_status(201)
end
end

Or in the case of query/url parameters.

it 'returns correctly status filtered tickets' do
status_filter = Ticket.statuses.keys.sample
query = {'status' => status_filter}
get :index, query
expect_status(200)
expect_json('*', status: status_filter)
end

If you have to send header parameters too along with query parameters, all you need to do is merge them into one hash and pass them as the second argument.

it 'returns correctly user filtered tickets' do
user_id_filter = User.includes(:tickets).where.not(tickets: {id: nil}).sample.id
query = {'user_id' => user_id_filter}
query.merge! auth_headers
get :index, query
expect_status(200)
expect_json('*.user', id: user_id_filter)
end

Working with devise_token_auth

devise_token_auth is my favourite gem for API authentication with rails and it provides a simple method to generate auth headers for your user; .create_new_auth_token.

You’ll probably have to authenticate a lot of requests so it’s better to not have to write this code in every controller. We can DRY this up by adding to our spec/rails_helper.rb file.

config.before(:all, type: :controller) do
@user = create(:user)
@user_auth_headers = @user.create_new_auth_token
end

I used factory_girl to create my user and you can do that however you like. What the above code does is simple, before all tests in a single controller it will create new authentication headers for your test users and you can pass that has as the second argument in your request to authenticate it.

it 'returns only tickets that belong to logged in user' do
get :index, @user_auth_headers
expect_status(200)
expect_json('*.user', id: @user.id)
end

Airborne API

Below are the things you should definitely check out in Airborne’s documentation.

Airborne provides the following to enable you to test the structure, values and data types of the elements in your API’s JSON response.

  • expect_json_types - Tests the types of the JSON property values returned. You can find the types that can be tested here.
  • expect_json - Tests the values of the JSON property values returned
  • expect_json_keys - Tests the existence of the specified keys in the JSON object
  • expect_json_sizes - Tests the sizes of the JSON property values returned, also test if the values are arrays
  • expect_status - Tests the HTTP status code returned
  • expect_header - Tests for a specified header in the response
  • expect_header_contains - Partial match test on a specified header

Usage examples can be found in the official documentation. It also has very good ways to handle testing array responses.

Furthermore, you can use path matching to test sub-properties within your JSON response.

Finally, here’s the source code to the example application I was building to sort of get familiar with Airborne, FactoryGirl & Rspec. It’s a pretty simple ticketing system API but you might find some gems there.

Get it? Gems?

That’s all for today. Hope this make your API testing a bit easier.

--

--

chunks of code*
chunks of code*

Published in chunks of code*

Articles chronicling stuff I do with code which may serve as tutorials (if anybody finds them useful)

Timi Ajiboye
Timi Ajiboye

Written by Timi Ajiboye

I make stuff, mostly things that work on computers. Building Gandalf.