Jan 20

Date published: 1-20-2014

Rails version: 4.0.0

Testing

One common task that developers should do when working on a project is to create tests that can be run to ensure that when we add new functionality to the project that it won’t break things that already exist. Rails already has a comprehensive test structure built in, and there are a ton of other resources that other developers have created to make testing easier. In this tutorial, I’m going to create tests for a basic User model using Rails’ test framework.

Create the project

Before we start writing the tests, we need a basic project that we can write the tests against. There is a large group of Rails developers that are adamant about doing Test-Driven Development (TDD) to ensure that when a project is finished it already has a mature test suite. TDD has a lot in its favor, but the problem I always had when I was trying to learn Rails from tutorials was I didn’t know what I was trying to do in the first place, so I didn’t know what tests to write. I’m assuming that anyone who is interested in this tutorial will have written some Rails code but has almost no knowledge of Rails testing. In this case I think it’s much more beneficial to learn how to add tests to an existing project. When you’re comfortable with how testing works in Rails, then you can decide if TDD is right for you.

Let’s start by creating our project. In the terminal window, run the following commands:

$ rails new rails_model_testing_example
$ cd rails_model_testing_example/

Let’s setup Git so we can save our work. Open the .gitignore file in the project directory and add the following at the bottom:

# Ignore other unneeded files.
doc/
*.swp
*~
.project
.DS_Store
.idea
public/system/

Open the terminal window and run the following commands:

$ git init
Initialized empty Git repository in /Projects/Rails/rails_model_testing_example/.git/
$ git add .
$ git commit -m "Initial commit"

Create a User scaffold

Since we’re not interested in what the actual pages look like, we’re going to have Rails generate a scaffold for us. Technically we don’t need all this, but it will give you some pages to look at in case you want to test out things like validation on your own. In the terminal window, run the following command:

$ rails generate scaffold User username:string password:string
$ rake db:migrate

You’ll see messages describing all the items that Rails is creating for us. We need to tell Rails what to show when we load the root page, so open config/routes.rb and add the following line:

root 'users#index'

Now if you start the Rails server, you’ll see a basic page that lists users and gives you the option to create new users. We can create users with any name or password, so we should add some validation to the User model. Add the following to app/models/user.rb:

class User < ActiveRecord::Base
  validates :username, presence: true,
    uniqueness: { case_sensitive: false },
    length: { minimum: 3, maximum: 16 }
  validates :password, presence: true,
    format: { with: /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).{6,}$/,
      multiline: true,
      message: "must be at least six characters and include one number, one lowercase letter, and one uppercase letter." }
end

So what are the validations doing? For the username, it has to exist, it has to be unique ignoring case (so there can’t be a user named Adam and another named adam), and it has to be between 3 and 16 characters long. For the password, it has to exist, and we’re using a regular expression to ensure that the password is at least six characters long and has at least one of a number, lowercase letter, and uppercase letter. Play around creating users in the browser to make sure that all the conditions we’ve assigned are being validated correctly.

Before we add test cases, let’s save our changes in Git. In the terminal, run the following:

$ git add .
$ git commit -m "Created a scaffold for User"

Writing the tests

Rails already created a few files for us to use to test out our User model. We’re going to talk about fixtures first, so open the test/fixtures/users.yml file and you should see this:

# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html

one:
  username: MyString
  password: MyString

two:
  username: MyString
  password: MyString

Let’s make some users of our own that we can use later while writing our tests. Change the values in this file to be:

one:
  username: Adam
  password: Password1

two:
  username: Doug
  password: Password2

When we run our tests, these fixture files will be loaded into the database automatically. We’ll see how this is useful when we try making users with the same names as a user in this fixture file. Rails already created a file to hold our tests at test/models/user_test.rb, so let’s look at it:

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  # test "the truth" do
  #   assert true
  # end
end

This is where we’ll add all our tests for the user model. You can see there is already a test there that is commented out that shows us the format of how tests should be written. The word test at the beginning of the line is a helper function that Rails provides. We can give it a descriptive name for the test so that when we see that name in the output when the test is run and easily figure out which tests have failed.

Let’s just go down the different validations in our model and write tests to make sure they’re working. Our first test should validate that a username is present and fail if one is not provided. Add this test to the file:

  test "should not save without username" do
    user = User.new(password: "Password1")
    assert_not user.save, "Saved the user without a username"
  end

In this test, we create a new user who has a valid password but no username. We call to assert_not to save the user. The assert_not method will pass only if the condition passed to it is false. In this case we want user.save to be false because we don’t want it to save a user who has no username. Let’s run this test and make sure it’s working. We need to make sure the test database is setup first, and then run our test:

$ rake db:test:load
$ rake test test/models/user_test.rb
# Running tests:
.
Finished tests in 0.357475s, 2.7974 tests/s, 2.7974 assertions/s.
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

So our test ran and passed, but how do we know that it’s actually working right? We can comment out the validations in the model and see that the test will fail. In app/models/user.rb, comment out the validations that test presence and run the tests again. For some reason, the test still passes! The reason is that we specified a minimum length for a username, and this validation fails and stops the user from being saved. If you remove the minimum length validation and run the test again, the user will save in the database and the test will fail.

Let’s write all the other tests for the username and add them to the test file:

  test "should not create a duplicate user" do
    user = User.new(username: "Adam", password: "Password1")
    assert_not user.save, "Saved a user with the exact same username as an existing user"

    user = User.new(username: "adam", password: "Password1")
    assert_not user.save, "Saved a user with a lowercase version of username of existing user"
  end

  test "should not have a too short username" do
    user = User.new(username: "x", password: "Password1")
    assert_not user.save, "Saved a user with a username that is too short"
  end

  test "should not have a too long username" do
    user = User.new(username: "12345678901234567890", password: "Password1")
    assert_not user.save, "Saved a user with a username that is too short"
  end

You’ll see that we’ve written tests for each of the different validations on username. If you run the tests as is, they should all pass. Try commenting out different validations in the user model to make sure that the tests fail when we save users with information that we don’t want.

Now we need tests for passwords. They look much the same as our tests for username, so add the following to the test file:

  test "should not save without password" do
    user = User.new(username: "User01")
    assert_not user.save, "Saved the user without a password"
  end

  test "password should be robust" do
    user = User.new(username: "User01")
    user.password = "PASSWORD1"
    assert_not user.save, "Saved the user with a password with no lowercase letter"

    user.password = "password1"
    assert_not user.save, "Saved the user with a password with no uppercase letter"

    user.password = "Password"
    assert_not user.save, "Saved the user with a password with no numbers"
  end

Run the test file now, and you’ll see that they all pass. You should also experiment by commenting out different validations and seeing that the tests fail. All our tests work, so that’s really good! Except that we still need to add one very important test. We want to make sure that when we create a valid user it gets saved correctly. Add this last test:

  test "should create a user with valid info" do
    user = User.new(username: "User01", password: "Password1")
    assert user.save, "Could not save a user with valid name and password"
  end

Run the tests again, and you’ll see that they all run correctly. There are many other things you can do with tests, like asserting that two items have the same value or that a value is not null. You can find more information about Rails testing at:

http://guides.rubyonrails.org/testing.html

That’s it for testing, so let’s save our changes in Git. Make sure to undo any changes you may have made in the User model, then in the terminal, run these commands:

$ git add .
$ git commit -m "Added tests for User model"

Source code for this tutorial can be found at:

http://github.com/wordplay/rails_model_testing_example

Leave a Reply

preload preload preload