Changes to test controllers in Rails 5

Abhishek Jain

By Abhishek Jain

on 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.

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.

1class ProductsControllerTest < ActionController::TestCase
2
3  def test_index_response
4    get :index
5    assert_response :success
6  end
7end
8

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.

1
2class ProductsControllerTest < ActionDispatch::IntegrationTest
3
4  def test_index
5    get products_url
6    assert_response :success
7  end
8end
9

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.

1
2class ProductsControllerTest < ActionController::TestCase
3  def test_index_template_rendered
4    get :index
5    assert_template :index
6    assert_equal Product.all, assigns(:products)
7  end
8end
9

But in Rails 5, calling assert_template or assigns will throw an exception.

1
2class ProductsControllerTest < ActionDispatch::IntegrationTest
3  def test_index_template_rendered
4    get products_url
5    assert_template :index
6    assert_equal Product.all, assigns(:products)
7  end
8end
9
10# Throws exception
11NoMethodError: assert_template has been extracted to a gem. To continue using it,
12  add `gem 'rails-controller-testing'` to your Gemfile.
13

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.

1
2class ProductsControllerTest < ActionController::TestCase
3
4  def test_show
5    get :show, { id: user.id }, { notice: 'Welcome' }, { admin: user.admin? }
6    assert_response :success
7  end
8end
9

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.

1
2class ProductsControllerTest < ActionDispatch::IntegrationTest
3
4  def test_create
5    post product_url, params: { product: { name: "FIFA" } }
6    assert_response :success
7  end
8end
9

This makes it easier to understand what arguments are being passed.

When we pass arguments without keywords arguments, then Rails logs a deprecation warning.

1
2class ProductsControllerTest < ActionDispatch::IntegrationTest
3
4  def test_create
5    post product_url, { product: { name: "FIFA" } }
6    assert_response :success
7  end
8end
9
10DEPRECATION WARNING: ActionDispatch::IntegrationTest HTTP request methods will accept
11only the following keyword arguments in future Rails versions:
12params, headers, env, xhr
13

Stay up to date with our blogs. Sign up for our newsletter.

We write about Ruby on Rails, ReactJS, React Native, remote work,open source, engineering & design.