April 19, 2016
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.
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.
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.
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.
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.
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
If this blog was helpful, check out our full blog archive.