I have a Rails server running on Thin and an async controller action. I would like to test it using RSpec, but I am getting an error:
Failure/Error: get :access_token
ArgumentError:
uncaught throw :async
This is a sample code:
class SampleController < ApplicationController
def sample
EM.defer do
render json: { response: 'Hello World' }
request.env['async.callback'].call response
end
throw :async
end
end
require 'spec_helper'
describe SampleController, :type => :controller do
it "hello world" do
get :sample
expect(JSON.parse(response.body)[response]).to eq('Hello World')
end
end
The reason why I get this error is because :async can only be handled by Thin server. There is no Thin server available in a vanilla Rails controller.
So tried Capybara:
describe "GET /sample", type: feature do
it "hello world" do
visit sample_path
expect(JSON.parse(page.body)['response']).to eq('Hello World')
end
end
# In spec_helper.rb
Capybara.server do |app, port|
require 'rack/handler/thin'
Rack::Handler::Thin.run(app, :Port => port)
end
But I still get the same error. I believe it is because Thin needs to be started in threaded mode; and capybara does no start it in such a way.
What is the proper way to test a controller's action that is using throw :async?
The action does work when I use a regular browser to get to it.
In order to test an asynchronous Ruby on Rails action implemented with Thin you will need to run the test using Thin. Otherwise it will fail, or be brittle – in case you try mocking things up.
So let's set it up using Capybara:
In 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
That sets the Capybara driver to be selenium, a real browser. The second part configures the Capybara server to be Thin.
The test should be written then as this:
describe SampleController, :type => :feature do
it "my test" do
visit sample_path
expect(page).to have_content('Hello World')
end
end
And this will make the test pass.