Learning Ruby by writing test scripts

NJ Pearman
EPFL Extension School
10 min readJun 12, 2018

Writing automated tests is an important part of being a responsible software developer. But it can be neglected at the start of the journey when learning to program. This is despite the fact that thinking about writing tests can help foster a good analytical approach to writing code, even for something as fundamental as building a single web page.

And certain types of tests can be written with just a small amount of code. This makes writing those tests approachable and also a good opportunity to learn about the syntax of a programming language, such as Ruby.

So writing tests could be useful to learn Ruby, and Ruby is used to build the Ruby on Rails framework. Let’s complete this virtuous circle: given that the Ruby on Rails framework comes with a lot of automated testing features out-of-the-box, it can be a good exercise to explore some of these tests in order to get some practise with writing Ruby, explore the structure of a Rails application, and think about what you are actually building.

In Rails, tests are filed under different categories. The most tangible category of tests are the system tests. These types of tests are end-to-end tests, which means that they run against a real browser by loading a web page and poking around, in the same way that we would try clicking and interacting with the page ourselves.

One of the great things about writing a system test is that they can test any page on the internet, not just the pages within a Rails app. So experimenting with automated system tests can be done without even building an app!

The perhaps controversial benefit is getting to use some Ruby code that gives tangible feedback without the need to dive into all of the complexities of the Ruby on Rails framework immediately.

Let’s take a look at how to set this up in a Ruby on Rails application.

The Capybara gem and Selenium WebDriver

We’re going to make use of the Capybara test tool in conjunction with another piece of software called Selenium Webdriver in order to load and run tests in the Firefox web browser. We’ll need to have all three of these things available — Capybara, WebDriver, and Firefox — before writing a test script.

The Mozilla Firefox web browser is available from the Mozilla website. We also have to download Selenium WebDriver.

Capybara is a Ruby gem, and is included as part of Rails as of Rails versions 5.1, so we don’t need to install anything new for that.

Selenium WebDriver is a neat bit of software that allows programs to launch and interact with a web page within a Firefox window. It’s available as a command line tool on GitHub. But we don’t need to download it, because Rails also includes the selenium-webdriver Ruby gem in the Gemfile!

selenium-webdriver is a Ruby gem that wraps the WebDriver program so that we can use it in Ruby programs without the need to install Selenium WebDriver separately! That’s a great example of Rails helping us out by providing useful tools in our application automatically. So we can get going with our first test script.

The first test script

A simple test script is shown below. It doesn’t interact with a Rails application; it opens duckduckgo.com and does some searching.

require 'application_system_test_case'Capybara.run_server = falseclass CapybaraTest < ApplicationSystemTestCase
test 'capybara works' do
visit 'http://www.duckduckgo.com'
sleep 5.seconds
fill_in 'q', with: 'Ruby on Rails'
sleep 5.seconds
click_on 'search_button_homepage', match: :first
click_on 'A web-application framework', match: :first
sleep 5.seconds
click_on 'Guides'
sleep 20.seconds
end
end

This script can go straight into a Rails app. Within the test/system/ folder in your Rails application, create a file called using_capybara_test.rb and copy all of the code above into that file.

Then there is one small configuration change to make: open the file test/application_system_test_case.rb and change the symbol :chrome to :firefox; Rails defaults to Chrome but we prefer using Firefox at the Extension School.

That is everything in place to run this first, simple system test. Run this command from your Rails app root in terminal: rails test:system.

If everything works, when you run the command, you should see a Firefox window magically open then navigate to duckduckgo.com, search for “Ruby on Rails” and start navigating around rubyonrails.org. Don’t click on anything in the window as it runs though; you’ll just interrupt the test script!

Well done! You’ve just run your first automated test!

So what are Capybara and Selenium Webdriver, in more detail?

Capybara is a gem that provides an easy interface to run commands in a virtual web browser. It is Ruby code that makes it straightforward to interact with a web page by finding particular text, clicking links and buttons, and more besides.

Selenium Webdriver is software that starts a Firefox process, opening a browser window, and then acts as the interface between Capybara and the window. It executes the commands within a Capybara script within a real Firefox window.

Capybara and Webdriver are a great combination that allows us to see test scripts running magically!

Controlling a page with Capybara

If we look at the sample test script from above, we can see the sorts of commands that Capybara lets us use. The script reads fairly well as plain English, indicating the intent of the whole test script.

  • sleep ... is used to slow the steps in the test down, as it can run so quickly that you won’t even notice what pages are being opened.
  • visit ... is used to open a particular URL directly.
  • fill_in ... is used to fill in a particular form field on the current page.
  • click_on ... is used to click on particular links or buttons on the current page.

Notice that one of the click_on commands includes a match: :first parameter. This is because the search results for "Ruby on Rails" returns a lot of links containing the term "Ruby on Rails", so we have to specify which match we want to click. Capybara throws an error in the console if we don’t include it and there are multiple matches.

Capybara has several commands in addition to the three above that make it really straightforward to start writing some automated system tests. The Capybara documentation is comprehensive and well written if you want to learn more.

Modifying a test script

Now is an opportunity to make some changes to the test script. We can modify the 'capybara works' test to do the following then run the command to execute the modified script.

  1. Search for ‘Cinque Terre’ instead of ‘Ruby on Rails’.
  2. Then click on the first search result matching ‘Wikipedia’.
  3. Then click on the first match for ‘Italian Riviera’.

Those are only small changes, but we get immediate feedback on what those changes do by running rails test:system.

Test cases

The block of code that starts test and finishes with end is one test case in the test script. A second test case can be added at the bottom of the script, inside the CapybaraTest class.

require 'application_system_test_case'Capybara.run_server = falseclass CapybaraTest < ApplicationSystemTestCase
test 'capybara works' do
visit 'http://www.duckduckgo.com'
sleep 5.seconds
fill_in 'q', with: 'Cinque Terre'
sleep 5.seconds
click_on 'search_button_homepage', match: :first
click_on 'Wikipedia', match: :first
sleep 5.seconds
click_on 'Italian Riviera', match: :first
sleep 20.seconds
end
test 'we can use capybara' do
# the second test case in our script!
end
end

This second test case, 'we can use capybara', can be populated with the following test steps:

  1. Open the page https://en.wikipedia.org/wiki/Internet, then sleep for five seconds.
  2. Fill in the input searchInput with the text 'Ruby programming language' , then sleep for five seconds.
  3. Click on searchButton, then sleep for five seconds.
  4. Click on the first match of 'Examples', then sleep for five seconds.
  5. Click on the first match of 'Hello world', then sleep for five seconds.

With that second test case in place, we’ll see our test count increase after running rails test:system, and we’ll have a test script that might teach us a bit about Ruby too.

How to be assertive!

So that’s two test cases in our first test script. These two test cases have been useful to practise some of the basic commands that Capybara provides. But both of the test cases are missing a crucial final part: assertions!

Assertions in automated tests are the steps that validate that something is as it is expected to be. They are the whole point of writing tests. We include assertions in our test scripts as the checks that the steps we’ve taken in the test produce the expected output or end-state.

We might assert that a search returns a number of results or that we have ended up on the correct page after submitting a form. Or, as in the example below, we might assert that a web page contains the content that we expect.

This is a modified version of the original test case at the start of the post. It now includes an assertion as the last step.

test ‘capybara works’ do
visit ‘http://www.duckduckgo.com'
sleep 5.seconds
fill_in ‘q’, with: ‘Ruby on Rails’
sleep 5.seconds
click_on ‘search_button_homepage’
click_on ‘A web-application framework’, match: :first
# we expect the Ruby on Rails homepage to include the text 'Contribute on GitHub'
assert page.has_content? ‘Contribute on GitHub’
end

With the addition of this assertion, assert page.has_content? 'Contribute on GitHub', we have a test that is testing for something specific. It is testing that the Ruby on Rails homepage contains some text matching “Contribute on GitHub”.

The assertion is being performed by the assert method. There are a whole set of variations on assert, which are provided by the minitest gem used underneath Capybara to run the tests. There is a comprehensive list in the documentation for minitest.

The basic assert method simply ensures that the value returned by the expression to the right of it is true. If it is true, the test is doing what we told it to expect and passes; if it is false, the test fails and we can see that things are not as we expect and can do something about it.

In the example above, we’re using the page object that represents the current page in our test script and one of its methods, has_content?, to ask whether the open page has the content 'Contribute on GitHub' within it. assert makes sure that this is true.

If the String 'Contribute on GitHub' is changed to 'Cookery on GitHub' then the test will fail when it is run, becaues that text is nowhere to be found on the open page.

Being more assertive

We can include more assertions in a single test script. This can be a good way to validate several things in one process. For example

  test 'search term is displayed' do
visit('https://wikipedia.org')
assert page.has_content?('The Free Encyclopedia')
fill_in('searchInput', with: 'Ruby on Rails').send_keys(:enter)
assert has_content?('David Heinemeier Hansson')
assert current_url.include?('Ruby_on_Rails')
end

This is a much more complex test script, so let’s break it down step by step.

First, the script visits the Wikipedia homepage, 'https://wikipedia.org'.

Then it asserts that the homepage contains the phrase “The Free Encyclopedia” somewhere.

Then it enters the search term Ruby on Rails into the search box. The script immediately submits this form using the .send_keys(:enter) method, which simulates a press of the enter / return key.

Capybara waits for the next page to load and then the test script asserts that the page includes the term “David Heinemeier Hansson” (the creator of Ruby on Rails).

The last step asserts that the full URL we have opened in the browser includes the page name for where we expected to end up, which should be Ruby_on_Rails. current_url is a handy method that Capybara provides for us in case we need to inspect the URL that’s currently open in the browser. It returns a String, and in this example we just use the include? String method to assert that the URL contains the substring that we expect.

One restriction with Capybara tests

Now, there is unfortunately a caveat to running system tests against external websites within a Rails application. In order to do so, Capybara must be configured to not run any tests against the Rails application. In the initial test script, there is a line of code that tells Capybara to prevent this: Capybara.run_server = false

This configuration disables the ability to test the Rails app itself. We remove that line of code in order to start writing tests against the app. But when that line is removed, it’s no longer possible to run tests against external websites.

So we can practise writing tests against external websites for a while but when it comes to writing real tests against our application, we’ll have to remove the run_server setting and remove the external website tests.

One way to keep practising how to write Ruby and test scripts against external websites is to keep a specific barebones Rails app to contain those tests. It’s also possible to use Capybara outside of Rails in smaller Ruby scripts once you’re comfortable with writing Ruby code.

Testing to learn, learning to test

Testing the applications that we build is a core part of web development. We do this manually as we build features, and writing automated tests supports how we test webpages and web apps.

But practising how to write automated system tests can be an alternative approach to learning how to use a programming language and how to think about the web pages that we need to build. When we run these tests with a piece of software like Webdriver, we get really tangible feedback on the code that we’re experimenting with. And this can be a rewarding experience.

Learn more!

Interested in learning more about Ruby, and Ruby on Rails? I teach Web Application Development at the EPFL Extension School, a certified online learning platform teaching digital skills in data science, web application development, and more. EPFL is one of the world’s leading universities and is ranked the “Number 1 Young University” by the Times Higher Education Ranking.

--

--