Older Rails Controller Tests
Learn about controller tests and controller tests methods provided by Rails older versions.
In Rails 5, two features of controller testing, one commonly used, one less so, were deprecated. This deprecation has the effect of limiting the scope of controller testing. In the eyes of the Rails core, this scope should be picked up by integration testing, though we’d suggest that some of it should be picked up by moving code out of the controller and unit-testing it. The following is a quick guide to what Rails 4 controller tests looked like, as we’ll probably see a bunch of them.
Controller test requests
Rails 4 provided the same set of methods we’ve covered for creating a request: get
, post
and their friends. However, the method parameters were different:
get :show, {id: @task.id}, {user_id: "3",
current_project: @project.id.to_s}, {notice: "flash test"}
The get
method
Here, the method name, get, is the HTTP verb being simulated, sort of. While the controller test will set the HTTP verb if for some reason we query the Rails request object, it does not check the Rails routing table to see if that action is reachable using that HTTP verb. As a result, we can’t test routing via a controller test. Refer back to the lesson “Testing Routes” to see how Rails provides a mechanism for testing routes.
First and second arguments
The first argument, in this case :show
, is the controller action being called. The second argument, {id: @task.id}
, is a hash that becomes the params
hash in the controller action. In the controller action called from this test, we would expect params[:id]
to equal @task.id
. The Rails form name-parsing trick is not used here. If we want to simulate a form upload, we use a multilevel hash directly, as in user: {name: "Noel", email: "noel@noelrappin.com"}
, which implies params[:user][:name] == "Noel"
in the controller.
Hash argument
Any value passed in this hash argument is converted to a string—specifically, to_param
is called on it. So we can do something like id: 3
, confident that it will be "3"
in the controller. This, by the way, is a welcome change in recent versions of Rails. Older versions did not do this conversion, which led to heads occasionally pounding against walls.
Third and fourth arguments
The third and fourth arguments to these controller methods are optional and rarely used. The third argument sets key-value pairs for the session object, which is useful in testing multistep processes that use the session for continuity. The fourth argument represents the Rails flash-object, which is rarely useful but if for some reason the incoming flash is important for our logic, there it is.
Access request object
We may occasionally want to do something fancier to the simulated request. In a controller test we have access to the request object as @request
, and access to the controller object as @controller
. (As we’ll see in the next topic, we also have the @response
object). We can get at the HTTP headers using the hash @request.headers
.
The xml_http_request
controller action method
There is one more controller action method: xml_http_request
(also aliased to xhr
). This simulates a classic Ajax call to the controller and has a slightly different signature:
it "makes an ajax call" do
xhr :post, :create, :task => {:id => "3"}
end
The method name is xhr
. The first argument is the HTTP verb associated with the xhr
call, and the remaining arguments are the arguments to all the other controller-calling methods in the same order:
- The
action
argument - The
params
argument - The
session
argument - The
flash
argument
The xhr
call sets the appropriate headers such that the Rails controller will appropriately be able to consider the request an Ajax request (meaning .js
format blocks will be triggered), then simulates the call based on its arguments.
Evaluating Controller Results
Rails 4 controller tests have a few other features that were deprecated in Rails 5. In Rails 4 we could use the render_template
matcher to assert which template rendered a method.
Instance values
Rails 4 also exposed an assigns
hash that had all the instance values that the controller passes to the view. This is deprecated now, but it was at one time a very common way to test Rails controllers. RSpec controller tests do not render the view by default, and we should use request or system specs if we would like that behavior.
Get hands-on with 1400+ tech skills courses.