Writing an RSpec test for an asynchronous action

Published on

In the previous article you learnt how to write a Rails action that can handle concurrent users, an asynchronous action. Today you are going to learn how to write an RSpec test for it.

This is the controller we want to write a test for:

class ApplicationController < ActionController::Base

  def sample
    EM.defer do
      render json: { response: 'Hello World' }

      request.env['async.callback'].call response
    end

    throw :async
  end

end

If this were not an asynchronous action, we would simply write a test like this:

require 'spec_helper'

describe ApplicationController, :type => :controller do
  it "hello world" do
    get :sample
    expect(JSON.parse(response.body)['response']).to eq('Hello World')
  end
end

But this won’t work because throw :async will kill the action right away. throw :async will make the interpreter go to this line of code defined in the Thin gem. The problem is that Thin is not loaded at all when running a controller test.

In order to properly test an asynchronous action you need to run the test using a real Thin server. You can use Capybara to do that.

Gemfile

gem 'thin'
gem 'capybara'
gem 'selenium-webdriver'

In spec/rails_helper.rb:

require 'capybara/rails'

Capybara.default_driver = :selenium

Capybara.server do |app, port|
  require 'rack/handler/thin'
  Rack::Handler::Thin.run(app, :Port => port)
end

Write the test:

describe ApplicationController, :type => :feature do
  it "my test" do
    visit some_path
    expect(page).to have_content('Hello World')
  end
end

And that’s it. Happy testing.