RSpec: testing controllers outside of a Rails application Jan 29, 2013

While testing my new gem, restful-controller, I needed to test a controller with RSpec outside of a Rails application.

It proved to be a little bit tricky and I'd like to share my experience.

Gems

We'll need actionpack and activesupport to emulate a Rails application. We'll need rspec-rails for actually testing Rails controllers.

gem . add_development_dependency 'rspec-rails' gem . add_development_dependency 'actionpack' gem . add_development_dependency 'activesupport'

Fixtures

Create a spec/fixtures directory with the following files:

application.rb

controllers.rb

fixtures/application.rb

This file will contain a minimum viable Rails application. Props go to the authors of the decent_exposure gem where I found this technique.

require 'active_support/all' require 'action_controller' require 'action_dispatch' module Rails class App def env_config ; {} end def routes return @routes if defined? ( @routes ) @routes = ActionDispatch : :Routing :: RouteSet . new @routes . draw do resources :posts # Replace with your own needs end @routes end end def self . application @app ||= App . new end end

fixtures/controllers.rb

Define a TestController which all other controllers will inherit from.

class TestController < ActionController : :Base include Rails . application . routes . url_helpers def render ( * attributes ); end end class PostsController < TestController restful_controller # This is the gem I'm testing, it will create all 7 default CRUD actions for me. end

A couple of things going on:

Firstly, we need to include url_helpers manually for redirects to work.

Secondly, if you don't have access to a controller's actions (in my case they are created by the gem, so I can't change them), you'll need to redefine the render method, otherwise you'll get an ActionView::MissingTemplate error.

This is due to the way RSpec and Rails work: even though RSpec won't render templates in controller tests, it still requires the file to be physically present. If you do have access to the actions, you can simply use render nothing: true .

Another way of solving this issue is to create an empty view file for every controller action out there, put it inside something like fixtures/views/posts/index.html.erb and then add the views path to the application config:

config . paths [ 'app/views' ] << "spec/fixtures/views" if Rails . env . test?

Needless to say, I prefer to simply redefine the render method.

Tests

Create spec/controller_test.rb :

require 'fixtures/application' require 'fixtures/controllers' require 'rspec/rails' describe PostsController , type : :controller do describe '#index' do get :index # ... end end

We need to use type: :controller because our file is not in the spec/controllers directory, so RSpec doesn't know it's a controller test. If you put the spec inside the controllers directory, you can omit it.

That's it, you can now test controllers without needing to create a full-blown dummy Rails application. The tests are very fast, too.

You're the 10880th person to read this article.