Introduction to testing in Rails

Testing your web applications can seem like a rather daunting and complex field at first. That’s how I felt when I started learning about it. It took a while to get comfortable with the lingo, and see how the different pieces related to each other. I made notes as I went and have collected them together below, into some semblance of an introductory post on the subject. Hopefully, it’ll lay out the lie of the land and point you in the direction of some useful resources.

I recently gave a short presentation on this subject entitled “An introduction to testing in Rails”, embedded here. Below the presentation, I’ll expand on some of these topics and show some basic tests.

Here are some notes and sample apps on GitHub that we used for testing in a recent meetup:

1. Why test?

Testing is a vitally important part of software development, especially as applications grow in size and complexity. Having a comprehensive test suite that you can run each time you (or someone else) makes changes to your codebase allows you to check that everything still works as expected. In addition, it serves as a permanent formal record of what “correct behaviour” is for your app, by permanently documenting conditions and expected responses.

So, all in all, it’s a good skill to develop and doesn’t need to be as scary as it first appears. Henceforth, I’ll be baking tests into my applications from the ground-up, even if the app I’m building is simple and it increases development time. It’ll help me continue learning how to test apps and develop better coding habits.

Consider this example below, where the GIF shows a simple web login form with validations on the presence and minimum length of the username and password. This is the sort of behaviour we want to test. Can you imagine how tedious it would be to keep manually typing in usernames and passwords and trying to submit the form, as shown in the GIF, each time you wanted to check the app still worked after an update? What if your application was ten times more complex? Or a hundred times? Then testing isn’t just nice to have, it’s critical to the development process.

2. A basic Todo App for testing

I created a very basic Todo app that allows users to add and delete Todo list items. It’s the C and D parts of a CRUD application. The point of this app is simply to be a uncomplicated platform for writing some Minitest and RSpec tests.

This is what the app looks like in my browser:

Todo app screenshot
Todo app screenshot

If you want to try some of the tests below, then feel free to fork the source code for the Todo app here on GitHub.

3. Minitest – Rails’ built in testing framework

Rails ships with its own testing framework, Minitest. The first few sections of the Rails documentation on testing introduce the subject and explain things in much greater detail than I do here.

When you spin up a new Rails app, a test folder will be created for you. Within this test folder will be subfolders for the different parts of your testing code (e.g. models, controllers, helpers, integration…) and a general test_helper.rb file (which holds the default configuration for your tests).

Rails will even go so far as building testing stubs for you (i.e. empty templates for your tests) and empty fixtures files (i.e. setting up sample data for your tests), when you generate models and controllers for example.

3.1 Example of model and controller tests with Minitest

Let’s create a test for our Todo model. This test will check the assertion that a todo entry should not save if it’s blank. In other words, users cannot submit blank todos, which is the behaviour we want.

Our Todo model, at todo_testing_app/app/models/todo.rb, looks like this:

:item is referring to the item attribute of my Todo object, which is a string containing the text of the todo item, e.g. the message “Clear out garage”. This validation uses presence: true to ensure that it’s non-blank when a Todo item is saved to the database.

I create a model test todo_testing_app/test/models/todo_model_test.rb and add the following code:

This test creates a new blank Todo and then attempts to save it. I use the assert_not statement to check that this is false however, i.e. the todo does NOT get saved. The test is actually checking that my Todo model validation works.

The code for this model test is on GitHub here.

It’s also possible to test our controllers. I’ve setup a test to get the index template and test it is successful and test the array of Todo data from the database is not empty. This basic controller test, saved at todo_testing_app/test/controllers/todos_controller_test.rb, looks like this:

Here the test requests the index page and tests the response is a success. It also tests that the :todos, the todo data from the database, is not nil, i.e. I have received the todo data for the index page.

The code for this controller test is on GitHub here.

3.2 How to run tests from the command line

The command to run your entire mini-testing suite is:

To run individual tests, for example the controller test above, the command is as before with the test path specified, as follows:

When we run these tests with the rake test command in the command line, the tests are executed and the results shown in the terminal window. A successful completion of a test is denoted by a “.” symbol in the terminal window. In our case we see two periods “..” to show that both our tests (model and controller) have passed, as shown in the image below. Notice also that the last line shows “2 tests, 3 assertions,…” because we had two “assert_” statements in our controller test.

Minitest console output
Minitest console output for a successful test

A failing test would show an output like this (for illustration, I have commented out the validation line in my todo model above to cause the test to fail):

Minitest failing output
Minitest console output for a failing test

Notice how the output is “F.” which indicates one test Failed (“F”) and one test passed (“.”). The output also shows our custom error message “Saved todo without item content”, that we specified in our todo_model_test.rb file. This is telling us that the test failed because the application is currently able to save blank todos, which is NOT the behaviour we want. I can uncomment the validation line in my model to fix this.

4. RSpec – popular testing framework

RSpec is a popular testing framework for Rails. It’s a powerful framework that allows you to create almost human readable tests.

RSpec doesn’t ship with Rails so we have to add it to our Gemfile and then install it, by adding the rspec-rails gem (source code here) as follows:

and then bundle install.

Finally, we need to initialize the spec directory with the following command in terminal prompt:

Now, you’ll see a folder called “spec” in your application’s root folder, which is where the RSpec framework lives. As per the Minitest section above, I’m going to use the same basic Todo app to create a model and controller test in RSpec.

I like to create a new branch for this, which I’ve called rspec_example, using the following command in the terminal:

This creates a new branch in git, where I can work on the RSpec test example.

The model and controller files are still the same so we will test the same behaviours, but write the tests out in RSpec.

4.1 Example of model and controller tests with RSpec

The RSpec model test, for the Todo model featured in part 3.1 above, is as follows:

The code for this model test is on GitHub here.

Line 6 is the meat of the test, where the behaviour is tested. This demonstrates RSpec’s powerful, almost plain English readability: “I expect the new todo not to be valid“.

Here is the corresponding controller test, which requests the index page and tests it is successful:

The code for this controller test is on GitHub here.

I’ve also added the assertions to test that the http status comes back as 200, meaning the request has succeeded, and that the index template is rendered correctly. The second test, “assigns todos”, creates a new Todo object with the item value “test” and then checks that the last item of the Todo array loaded by the index page is equal to this new Todo item we just created.

4.2 How to run tests from the command line

The command to run your entire rspec testing suite is:

Similarly, to run individual tests, e.g. a controller test, the command is as before with the test path specified, as follows:

The output from RSpec is similar to that from Minitest, as you can see in the following snapshot of a successful test outcome:

RSpec console output for a successful test
RSpec console output for a successful test

4.3 Taking RSpec further

I followed this set of posts from Everyday Rails for a deeper dive into using RSpec to test my Rails apps:

Part 1 – Introduction to testing
Part 2 – Setting up RSpec
Part 3 – Model testing with Factory Girl
Part 4 – Testing Rspec Controllers
Part 5 – Testing Series Rspec requests

However the series is from 2012, and since these tutorials were published, there have been some changes to the RSpec syntax which I had to be mindful of that as I wrote my own tests.

It’s still a fine resource however, provided you keep this caveat in mind.

I went through these tutorials, trying to also implement these RSpec best practises as I went, to create a more comprehensive testing suite for a basic contacts app.

As an example, within my controller test, I run some tests to check the “post” action works as expected when creating new contacts in a contacts app I created.

In essence, what I do is run four tests, where two have valid attributes and two have invalid attributes, as follows:

Tests with valid attributes:

  • Expect creating a new valid contact to change the contact count by 1
  • Expect to then redirect to this new contact’s page

Tests with invalid attributes:

  • Expect creating a new invalid contact to not change the contact count (i.e. the invalid contact is not saved to database)
  • Expect the response to then render the new template again (i.e. show the new template again to present another opportunity to add a contact)

This is the corresponding RSpec code for these four tests:

The full GitHub code for this controller is here, and for the contacts app and RSpec testing suite is here on GitHub.

5. Glossary of some other testing terms

Unit Testing: testing small, specific piece of code to see if it behaves as expected.

Integration Testing: testing pieces of code together to see if they behave as expected and work in harmony together.

Acceptance Testing: testing from the perspective of a user, to ensure the application behaves as a user would expect. E.g. hit this url, fill in this box, hit this button, expect this output.

Test Driven Development (TDD): Test Driven Development is a software development process in which the developer writes tests for a specific feature or behavior first (which obviously then fail initially), before writing code to pass the tests. It was developed by Kent Beck in early 2000’s. The excellent Michael Hartl Rails Tutorial teaches test driven development from the beginning and is highly recommended.

Behavior Driven Development (BDD): Behavior Driven Development is a software development process that evolved out of TDD but includes specific reference to the behaviors of the unit being tested.

Fixtures is a fancy word for sample data, to be used to test your application.

Factory Girl is a fixtures replacement, with straightforward definition syntax, created by Joe Ferris and maintained by Thoughtbot. Factory Girl Rails (gem here) provides Rails integration for Factory Girl.

Cucumber: Cucumber is a high level testing framework for Rails, allowing you to do TDD/BDD on your Rails apps, with the ability to write tests in English.

Capybara is a gem which simulates how a real user interacts with your app. The readme file on GitHub is a great place to start.

6. Further Resources

6.1 Intro:

Article on testing from Code Newbies – recommended

Podcast from Code Newbies on testing.

6.2 Official docs:

Guide to testing in Rails with Minitest – recommended.

RSpec documentation.

6.3 Going further:

The magic tricks of testing from Rails Conf 2013

Thoughtbot on how they test Rails applications

RSpec best practises with Better Specs.

Interesting opinion piece on choosing Minitest over RSpec.

Leave a Reply

Your email address will not be published. Required fields are marked *