Integration tests for web services using RSpec

Recently I had to implement a REST web service for a project in Rails. I've been using RSpec for every kind of tests for a long time but, still, wasn’t sure on what was the best way to use it to test a web service.

Capybara has always been my first choice for integration tests, but it doesn’t make much sense if your views are just plain JSON. Thus, I went for a leaner approach taking advantage of Rack::Test::Methods.

In this post I will explain my approach to test a web service. I won't get into any details on how the web service in question could or should be implemented.


The first thing to setup is the filesystem. There are infinite ways to do this, I prefer to keep the API tests isolated from the rest of the application.

├── api
├── controllers
├── factories
├── features
├── models
├── spec_helper.rb
└── support

I guess it would also make sense to decide to put the tests for your API inside the features folder, but I prefer to create a separate api folder. It just feels cleaner.

There's only one more step to complete the setup. We need the Rack::Test::Methods module in our tests. For convenience let's add it to the spec_helper.rb.

# ...
RSpec.configure do |config|  
  config.treat_symbols_as_metadata_keys_with_true_values = true
  config.include Rack::Test::Methods, api: true
  # ...

The line config.treat_symbols_as_metadata_keys_with_true_values = true is needed so we can write describe 'some spec', :api instead of describe 'some spec', api: true. Click here for more info on this subject.

That's it! No more setup needed, let's write some specs!


For the sake of example let's say we are building an application that, for some reason, manages products.

I always create one file for each endpoint. Since we're managing products, there should be an endpoint for products. Therefore there is a spec file named products_spec.rb inside our spec/api folder.

require 'spec_helper'

describe '/api/products', :api do  
  # ...

The main describe block clearly states that we're describing the /api/products endpoint spec in this file. The describe block as a type of api, the type property is not very important in this case, but it can be used to create filters. More info about that here.

Inside the main describe block we can describe the several actions available at this endpoint, like index, show... This is where all the magic happens.

describe '#index' do  
  before { get api_products_path }

  it { last_response.status.should eq 200 }

  it 'should return 10 products' do
    products = JSON.parse last_response.body
    products.count.should eq 10

On this example we are testing the index action for the /api/products endpoint. We have a before hook that sends a get request to api_products_path. This means that get api_products_path will execute before each it block.

My favourite part of it is that we have the method last_response. This method returns the response for the last request made in the script and we can use it to access the status of the response, the body, header, etc... All courtesy of Rack::Test::Methods. You can check the documentation for other methods that you may need.

Btw, the get method is also part of the Rack::Test::Methods module. There's also methods for the other types of requests, post, put, patch, delete, etc...


  • Integration tests for you API should be isolated from the rest of your test suite;
  • Each spec file should have the specs for an endpoint;
  • You don't need a complex framework for this kind of tests, Rack::Test::Methods works just fine;

[EDIT 24/08/2013] Added suggestion made by @yoz.