The ABC of Unit Testing in Rails, Part 2.
Controller Testing
The same way we created unit tests for our models in my previous post The ABC of Unit Testing in Rails, Part 1 we can do the same for our controllers. When we think about testing our controllers we can think about functional tests, since they test the individual actions your controller is in charge of performing.
**Important
Before continuing with this blog make sure to follow the steps on The ABC of Unit Testing in Rails, Part 1 since this is post is going to pick up where we left off on the previous post.
Creating our test
To create our controller test we just need to run the command
rails g unit_test:scaffold <name_of_resource>
in our case rails g unit_test:scaffold User
.
It generates two tests one for suite and we are going to focus on the controller test. When you open users_controller_test.rb
you will find default tests for all our restful routes, for now, let's just focus on the index, show and new actions, and comment the rest of the tests.
If we run our test rails test test/controllers
we’ll get the following error message.
This is due since we have not created our controllers and routes. Let’s create our controller users_controller.rb
in app/controllers
and create our routes in routes.rb
located in app/config/
class UsersController < ApplicationController
def indexenddef showenddef new
end
end
Let’s create our routes in routes.rb
located in app/config/
.
//inside routes.rb
Rails.application.routes.draw do# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.htmlresources :users, only: [:index,:new,:show]end
Last but not least, let’s create the views for the routes we are going to test. Inside app/views/users
create the files index.html.erb
, show.html.erb
, and new.html.erb
.
If we run the test again all of our tests should pass.
Now that we passed this test, let’s work on the test for create and destroy actions. First, let’s uncomment the lines for these tests.
And change the line assert_difference('User.count')
to assert_difference('User.count',1)
and let's add the parameter necessary to create a user in the line below.
Then, let's add our routes to routes.rb.
//inside routes.rb
Rails.application.routes.draw do# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.htmlresources :users, only: [:index,:new,:show,:create,:destroy]end
And add the create and destroy action to our User Controller Class.
class UsersController < ApplicationController
def index end def show end def new
end def create end def destroy endend
Different than the previous functions which only job was to render a view. The create and destroy action affects the records in the database, therefore why the test uses assert_difference
to look for the difference in the number of records. If we run the test right now, they would fail since our create and destroy actions are defined as empty functions.
Create Action
In order for the create action to pass the test, it must create a record and redirect to the show page of the new record.
Let's define a private user_params
function to whitelist the parameters necessary to create a user.
Then inside the create action.
Destroy Action
In order for the destroy action to pass the test, it must delete a record and redirect to the index page.
To achieve this we are going to modify the test suite setup method and create a user to later delete during this test.
In users_controller_test.rb
Inside users_controller.rb
inside the destroy method.
Now if we run our tests, we will successfully pass them all.
Conclusion
Controller testing might seem unimportant since controller methods tend to be thin and dumb it is still important to write tests for them to ensure they’re calling the business or service objects correctly and passing the correct parameters, redirecting to the right path, etc… As a bonus, just as in my previous post I left some of the testing undone for the edit and update actions, if you like the post I encourage you to leave what you believe would be the code for the test case and controllers in the comments.