Writing full and complete tests for a Rails application requires testing each of the parts of the MVC pattern. For the Model component, you test that the methods in your models do all the things you want them to and that the relationships between your models work properly. I have gone over the basics of how you can write tests for you models in my previous two blog posts. In this one I will talk about how to start testing the View part of the MVC architecture by talking about how to test the way a user interacts with the webpages in your application.
But first, I’m going to talk a bit about ways to figure out how many tests you should write. It is of course impossible to test for every possible thing that could happen when someone uses your app, and too many tests would take too long to run. But you also want to make sure you have enough tests to cover each range of possibilities. A good way to do this is by using edge cases and partitioning.
Edge cases occur at an extreme, like a maximum or a minimum. For example, an empty string would be an edge case for strings. Partitioning is separating the inputs you are testing into groups of a similar type. An example of partitioning when testing numbers would be separating inputs into negative, positive, and zero. Then you only have to write three tests, each using one number in each partition. Using these methods you can write tests that hopefully cover every type of input a user could give while not writing a whole bunch of unnecessary tests.
Getting back to Rails, the way to write tests for your views in Rails is system testing. All the files for this are kept in the system folder under the test directory. Now, unlike all the other types of tests I’ve talked about so far, a system test file is not generated by
rails g resource. But Rails does provide a generator to make system tests for each of your models:
~ // ♥ > rails g system_test authors invoke test_unit create test/system/authors_test.rb
This command creates a file for system tests for the authors model (with a sample method). But since I used
rails g scaffold, and
scaffold does generate system test files, my authors_test.rb file already had a variety of system tests written for authors. The only thing I had to change was the line in the setup method to reference the
rowling fixture I had written earlier:
# in system/authors_test.rb require "application_system_test_case" class AuthorsTest < ApplicationSystemTestCase setup do @author = authors(:rowling) end test "visiting the index" do visit authors_url assert_selector "h1", text: "Authors" end test "creating a Author" do visit authors_url click_on "New Author" fill_in "Age", with: @author.age fill_in "Name", with: @author.name click_on "Create Author" assert_text "Author was successfully created" click_on "Back" end test "updating a Author" do visit authors_url click_on "Edit", match: :first fill_in "Age", with: @author.age fill_in "Name", with: @author.name click_on "Update Author" assert_text "Author was successfully updated" click_on "Back" end test "destroying an Author" do visit authors_url page.accept_confirm do click_on "Destroy", match: :first end assert_text "Author was successfully destroyed" end end
System tests are noticeably different from the other tests I have talked about before. For one thing, they are not automatically run when you run the
rails test command. You have to either specify the system test file you want to run, like rails test
test/system/authors_test.rb or, to run all you system tests, in the command line run:
~ // ♥ > rails test:system
Another thing different about system tests is how you are not just using assertions anymore, you are using Capybara to interact with the webpage. From the tests in authors_test.rb above, you can see from the intuitively named methods that you can tell the test to visit a given url path, click on certain buttons, and fill in form inputs. All of this is supposed to mirror how a user would interact with a webpage. For more information on what kinds of methods Capybara has, you can look through their documentation.
Another interesting thing about system tests is that when they are run, they use a browser window. When I ran
rails test:system it opened a new google chrome window for each of the tests, and if you watch one window you can see it doing the things defined in the test, like navigating from page to page and filling in and submitting forms. But it all moved so fast it was difficult to see what happened, and to help with this Rails has the ScreenshotHelper. You can add
take_failed_screenshot at any point in your tests to take a screenshot of the browser window running the test. ScreenshotHelper is very helpful in debugging how something went wrong.
Now, the reason my system tests opened a chrome window was because it was configured that way. There is a file in the top level of the test directory that is generated every time you start a Rails application (as long as it’s not an API) called application_system_test_case.rb. This file is for configuring your system tests and the default configuration looks like this:
require "test_helper" class ApplicationSystemTestCase < ActionDispatch::SystemTestCase driven_by :selenium, using: :chrome, screen_size: [1400, 1400] end
The “driver” in
driven_by determines where the tests are run, in a browser window or in your terminal or another type of window. You can also specify what browser you would like to use with
screen_size argument defines what size you’d like the browser window to be. This argument can be very useful if you want to test how your app looks on mobile. If you plan on having your app used on mobile you can create a separate file with configurations for the mobile
screen_size and have some tests for mobile that
require and inherit from your mobile configured file instead of ApplicationSystemTestCase.
I think it’s clear that system tests are a very important part of thoroughly testing a Rails application. I have now covered how to start writing tests for both the M and V parts of the MVC framework. In my next post I’ll go into testing the Controller component of the MVC pattern by going into how to begin writing tests for your controller actions.