Using Contest's context() in Rails' controller tests

Suraj N. Kurapati

Contest is a Ruby library that brings Shoulda-style nestable contexts, in the form of context() methods, and an easier way to define test blocks, in the form of should() methods, to Ruby’s Test::Unit testing framework.

When used inside a Rails’ controller test, Contest’s context() method raises the following error:

RuntimeError: @controller is nil: make sure you set it in your test's setup method.

For example, the following controller test triggers the error:

require 'test_helper'
require 'contest'

class ExamplesControllerTest < ActionController::TestCase
  context "index action" do
    should "return a list of examples" do
      get :index, :format => :json
      assert_response :success
    end
  end
end

This happens because, internally, the context() method creates a new Test::Unit::TestCase-derived class whose name does not contain that of the parent controller test (which is ExamplesController in the above example). As a result, Rails complains when it is unable to automatically set the @controller instance variable inside the block passed to the context() method.

Robert Gleeson suggested a workaround where you manually assign the correct value to the @controller instance variable on behalf of Rails in the created context’s setup() block:

context "index action" do
  setup do
    @controller = ExamplesController.new
  end
  # ...
end

I automated the application of this workaround through the following monkeypatch, which you can add directly to your Rails’ test helper file:

class ActionController::TestCase
  def self.context *args, &block
    super *args do
      setup do
        # establish the rails controller being tested
        # https://github.com/citrusbyte/contest/issues/5#issuecomment-677003
        ancestors = self.class.ancestors
        test_case = ancestors[ancestors.index(ActionController::TestCase)-1]
        @controller = test_case.controller_class.new
      end
      class_eval &block
    end
  end
end

And with that, you are now riding the Rails, Contest style! :-)