Intro to Testing With Rails: System Tests
Tags: testing, rails
Published: 2020-11-24 01:20:55

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](https://github.com/teamcapybara/capybara#the-dsl).

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_screenshot` or `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 `using`. The `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.