Using Contest's context() in Rails' controller tests
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! :-)