Ctrlspecs

What's the responsibility of a controller?

Render. Redirect. Flash.

I'd argue that those are, in fact, the only responsibilities controllers have. Now, I realize controllers' actions are often burdened with resource creation, deletion and updating, but let's keep it real here - all this logic belongs to another object, usually a service. And indeed, most of the non-trivial, well designed applications use service objects, instead of cramming everything inside poor controllers.

Installation

Add this line to your application's Gemfile:

gem 'ctrlspecs'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ctrlspecs

In your rails_helper.rb add:

RSpec.configure do |config|
  config.extend Ctrlspecs
  ...
end

Usage

Ctrlspecs gives you a few helpers, which are basically just shared examples testing what your typical controller does:

  • what template does it render
  • where does it redirect to
  • what message does it flash
  • what service does it call
subject { post :create, params } # subject is required

it_renders_template :create
it_redirects_to 'resources_path(assigns(:resource))' # the path argument will be evaled
it_flashes notice: I18n.t('shared.created')
it_calls_service SuchService

Ctrlspecs also provides contexts for testing failures:

mock_save_record_failure
mock_service_returning_error, VeryService, Response::Error

So, how does your typical controller action spec look like now?

require 'rails_helper'

describe PostsController do
  describe 'POST #create' do
    let!(:params) do
      { post: { content: 'such content, wow' } }
    end
    subject { post :create, params }

    context 'success' do
      it_redirects_to 'post_path(assigns(:post))'
      it_flashes notice: I18n.t('shared.created', resource: 'Post')
    end

    context 'failure' do
      mock_save_record_failure
      it_renders_template :new
    end
  end

  describe 'GET #index' do
    subject { get :index }
    it_calls_service Posts::GetAllTheFrigginPosts
  end
end

Contributing

  1. Fork it ( https://github.com/mwsteb/ctrlspecs/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request