Making Your System Tests More Readable

When I ramp onto a new project, one of the first things I do is to look at the system test suite. Because system tests exercise the entire system, they are a great place to document the intent and assumptions built into your application. When they are written well, they can be a source of documentation and a tool to educate others on the behaviors of an application.

Abstract Away DOM Interactions

Most web testing tools, such as Capybara, include built-in helpers to interact with the DOM. The helpers are powerful and necessary, but they make it more difficult to understand the intent of an action. For example, if we have a button:
<button class="my-cool-button">Delete</button>
that we need to click in a test, we could write:


click_button "Delete"

It’s fairly readable, but what if we have two buttons on the page with the same text? Well, we can add this:


within ".do-thing-container" do
  click_button "Delete"
end

We just lost some readability, and we are making the intent of our test harder to understand. If we abstract those interactions away, we can keep things more clean.

Let’s pretend we’ve defined a helper that uses regular expressions to map phrases to DOM elements. We could replace the above with this:


click_area "remove student button"

We just made our intent clearer. Instead of trying to surmise which “delete” we are talking about, we know we are clicking on the area that represents the “remove student” button. This approach also hides the structure of the DOM from the implementation of the system test. Adding something like this helper will do a lot to improve system test readability.

Write Cucumber-Like Helpers

Cucumber tests were developed to provide documentation that you could share with customers to show them that all business cases were covered by automated tests. Now, that exercise may be overkill, but writing Cucumber-style methods makes it much easier for other developers to read and understand system tests.

For example, building on what we learned above, we could have a system test that looks like this:


scenario "A teacher assigns a test to students" do
  click_area "select assignment button"
  click_area "assignment titled 'Do Things'"
  click_area "student with name 'Jeff'"
  click_area "Make assignment button"
  expect(page).to have_content "Assignment was successful"
  expect(area("Number of tests for 'Jeff'")).to eq(1)
end

Someone new to the project might be able to look at this code, bounce back and forth to look at the UI, and eventually understand what is happening. But, if we write this in more of a Cucumber fashion, the intent is even easier to understand. A system test I wrote might look like this:


scenario "A teacher assigns a test to students" do
  when_teacher_assigns_do_things_to_jeff
  see_the_assignment_was_successful
  see_jeff_received_the_assignment
end

Now, it’s easy to understand this test is exercising an assignment workflow for an application.

These are two approaches I’ve found to be very useful when writing clean and readable system tests. What steps have you taken to make your system tests more readable?