Upgrading Rails 5 Controller Tests

Posted by Brian in News (July 3rd, 2016)

If you're embarking on a Rails 5 upgrade, or just trying to figure out what's involved, you'll find lots of pieces of information spread around. But I'm summarizing this here for my own benefit and for others.

No more unit tests for controllers

Rails 5's controller tests are now integration tests. This means you lose the ability to interact with many of the internals. And if you've built apps of any reasonable size with controller tests, you'll find a lot of the hacks you've been using are no longer there.

URLS, not actions, are required.

Instead of

get :index

you'll need to do

get root_path

In other words, you'll use full URLs in your tests, or use the URL helpers for your routes.

And when sending params for looking up records, instead of

get :index, {id: 1}

You'll want to use

@post = posts(:one) # if using a fixture
get posts_path(@post)

You must use named parameters for params

So if you are doing a create, instead of doing this:

post posts_path, {post: { title: "First post", body: "This is the body"}

you'll need to make it less ambiguous, as these helpers now require named parameters:

post posts_path, params: {post: { title: "First post", body: "This is the body"} }

No more access to request

The request object has been removed. If you've been using it to set headers, that's no longer possible.

So instead of trying to use

request.env["HTTP_AUTHORIZATION"] = something....

You'll need to send it along on each request:

get admin_path, headers: {'HTTP_AUTHORIZATION' => something }

Mo more access to session

If you're used to sending along session data with your requests, like for authorization, know that you can't do that either. Instead, you'll have to actually hit the login page of your site. DHH explains this in his response to this Github issue.

  def sign_in_as(name)
    post login_url, params: { sig: users(name).perishable_signature )
  setup { sign_in_as 'david' }

This is exactly how it's done in integration tests, so this isn't a surprise, but no doubt your existing apps have tests that directly pass the session on each request. You'll need to change those.

No more access to assigns and assert_template

If you’ve used `assigns` to access instance variables, or used `assert_template` to ensure a specific template was rendered, those tests will no longer work. There is a gem that brings this behavior back, but I won’t link it because I don’t trust that it’ll be maintained, and the way forward is clear – use `assert_select` to look for things on the page or in the response.

The days of controller tests as units are over, and like me, you may be now questioning the value of controller tests at all going forward, since they are just integration tests.