This blog is part of our Rails 5 series.
In Rails 5, controller tests have undergone some major changes. In this blog post, we will walk through some of those changes.
ActionController::TestCase is deprecated
In Rails 5, controller tests are generated with superclass
ActionDispatch::IntegrationTest instead of ActionController::TestCase which
is deprecated (Link is not available) . It will be moved into a separate gem in
Rails 5.1 .
Rails 5 will use ActionDispatch::IntegrationTest by default for generating
scaffolds as well
controller tests stubs.
Use URL instead of action name with request methods in Rails 5
In Rails 4.x, we pass controller action as shown below.
class ProductsControllerTest < ActionController::TestCase
def test_index_response
get :index
assert_response :success
end
end
But in Rails 5, controller tests expect to receive URL instead of action (Link
is not available). Otherwise test will throw exception
URI::InvalidURIError: bad URI.
class ProductsControllerTest < ActionDispatch::IntegrationTest
def test_index
get products_url
assert_response :success
end
end
If we are upgrading an older Rails 4.x app to Rails 5, which have test cases
with superclass ActionController::TestCase, then they will continue to work as
it is without requiring to change anything from above.
Deprecation of assigns and assert_template in controller tests
In Rails 4.x, we can test instance variables assigned in a controller action and
which template a particular controller action renders using assigns and
assert_template methods.
class ProductsControllerTest < ActionController::TestCase
def test_index_template_rendered
get :index
assert_template :index
assert_equal Product.all, assigns(:products)
end
end
But in Rails 5, calling assert_template or assigns will throw an exception.
class ProductsControllerTest < ActionDispatch::IntegrationTest
def test_index_template_rendered
get products_url
assert_template :index
assert_equal Product.all, assigns(:products)
end
end
# Throws exception
NoMethodError: assert_template has been extracted to a gem. To continue using it,
add `gem 'rails-controller-testing'` to your Gemfile.
These two methods have now been
removed from the core and moved to
a separate gem
rails-controller-testing.
If we still want to use assert_template and assigns, then we can do this by
adding this gem in our applications.
Reasons for removing assigns and assert_template
The idea behind the removal of these methods is that instance variables and which template is rendered in a controller action are internals of a controller, and controller tests should not care about them.
According to Rails team, controller tests should be more concerned about what is the result of that controller action like what cookies are set, or what HTTP code is set rather than testing of the internals of the controller. So, these methods are removed from the core.
Use of Keywords arguments in HTTP request methods in Rails 5
In Rails 4.x, we pass various arguments like params, flash messages and session variables to request method directly.
class ProductsControllerTest < ActionController::TestCase
def test_show
get :show, { id: user.id }, { notice: 'Welcome' }, { admin: user.admin? }
assert_response :success
end
end
Where { id: user.id } are params, { notice: 'Welcome' } is flash and
{ admin: user.admin? } is session.
This becomes confusing sometimes, as it is not clear which argument belongs to which part.
Now in Rails 5, request methods accept only keyword arguments.
class ProductsControllerTest < ActionDispatch::IntegrationTest
def test_create
post product_url, params: { product: { name: "FIFA" } }
assert_response :success
end
end
This makes it easier to understand what arguments are being passed.
When we pass arguments without keywords arguments, then Rails logs a deprecation warning.
class ProductsControllerTest < ActionDispatch::IntegrationTest
def test_create
post product_url, { product: { name: "FIFA" } }
assert_response :success
end
end
DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept
only the following keyword arguments in future Rails versions:
params, headers, env, xhr