Valerie Foster
Posted on November 24, 2020
This is the final part in my series of posts about testing with Rails. Previously, I talked about getting started with Rails testing, and I talked about writing tests for the Model and View parts of the MVC pattern. This post will talk about how to write tests for the Controller part of the MVC framework, testing your routes and controller actions. This is the last important piece of testing your Rails application.
Testing can open up a whole new way to think about developing code. One way to think about development is using something called Test Driven Development, or TDD. TDD is a software development process where you write a test with the desired behavior you want your application to have, then you write the code to make the test pass. This way of developing code can be very efficient. For one thing, it forces you to think about what functionality you want to add and exactly what result you want it to have. This also makes sure you don’t have any pieces of untested code — if you write your tests after the fact, you could forget to check the functionality for some sections of code.
Also, TDD can make it easier to figure out why your tests are failing. When you write functionality to pass the test you just wrote, you have a better idea of what sections of code come into play in making your test pass. Therefore, if the test is still failing after you’ve added the code to make it pass, you know where in your code to look to find the part that doesn’t work. This can also help you realize if your tests are trying to cover too wide a range of functionality, and if you might want to split the test into multiple parts to test smaller pieces of functionality.
Now, about Rails controller tests. Controller tests are used to verify that all of your controller actions do as intended as well as checking that your routes function correctly. So naturally, they rely heavily on request types like get
, post
, patch
, and delete
. All of these request types have equivalent methods that you can use to check if the result of the request is successful, and that a post
, patch
, or delete
updated the database and redirected to the proper page.
An example test for checking that the index
action in the authors_contoller works could use the get
method with the path for the Author
’s index page, then check that the url response is a success. The method would look like this:
test "should get index" do
get authors_path
assert_response :success
end
In contrast to the get
method, post
and patch
need to use parameters to give the method the new information to create or update a record with. You would pass these params to the post
method like this:
post authors_path, params: {
author: { name: "Philip Pullman", age: 73 }
}
Another thing controller tests can check for is the three Hash objects you have access to after a request has been made and processed; cookies
, flash
, and session
. Say, for example, in my author’s create action I used a flash notice to add an alert whenever an author was created successfully. I could check it was working by adding a line like this to my test for creating new authors:
assert_equal 'Author was successfully created.', flash[:notice]
One more useful thing that controller tests give you is access to three instance variables after a request is made: @controller
(the controller processing the request), @request
(the request object), and @response
(the response object). So, another way you could check that the index
action was working would be like this:
test "should get index" do
get authors_path
assert_equal "index", @controller.action_name
assert_match "Authors", @response.body
end
Of all of the different things I have talked about that you can add to your controller tests, you will probably find some more useful than others. I have only used a few in the example controller test file that I have here (the file is mostly the same as the one generated from the scaffold
command I ran for authors
):
# in controllers/authors_controller_test.rb
require 'test_helper'
class AuthorsControllerTest < ActionDispatch::IntegrationTest
# method that is called before every test
setup do
@author = authors(:rowling)
end
test "should get index" do
get authors_url
assert_response :success
end
test "should get new" do
get new_author_url
assert_response :success
end
test "should create author" do
assert_difference('Author.count') do
post authors_url, params: {
author: { name: "Philip Pullman", age: 73 }
}
end
assert_redirected_to author_url(Author.last)
end
test "should show author" do
get author_url(@author)
assert_response :success
end
test "should get edit" do
get edit_author_url(@author)
assert_response :success
end
test "should update author" do
patch author_url(@author), params: { author: { age: 56 } }
assert_redirected_to author_url(@author)
end
test "should destroy author" do
assert_difference('Author.count', -1) do
delete author_url(@author)
end
assert_redirected_to authors_url
end
end
With all of the types of tests I have covered in my four blog posts on Rails testing, I have given a pretty thorough introduction to writing tests for Rails applications. I have also given reasons for why writing tests is useful and practical when creating any kind of application. Ultimately, I have tried to convince you that you should consider writing tests for the next Rails app you create. Tests are very useful, and I hope my posts have made it seem doable to implement them in Rails.
Posted on November 24, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.