How to test your Rails app

RSpec

RSpec is a testing tool for Ruby, created for behavior-driven development (BDD). It is the most frequently used testing library for Ruby in production applications. Even though it has a very rich and powerful DSL (domain-specific language), at its core it is a simple tool which you can start using rather quickly.

Apart from the Rspec gem we will use the following tools:

Database Cleaner

Before each test case we need to make sure that our database is clean. If we leave information on the database, this information can cause unwanted failures.

FactoryBot

To test our platform we need data. There are two ways to create data for our tests without creating everything manually: Fixtures and Factories (Please look up the difference). At eagerworks we use factories to create our test data.

Faker

We have factories but adding emails, names, addresses to our factories can be a pain in the ass. That is why we use faker gem to make it easier.

Shoulda Matchers

Shoulda Matchers provides one-liners to test common Rails functionality. We will use Shoulda Matchers to test model associations and validations.

Capybara

Capybara helps us test web applications by simulating how a real user would interact with your app. This tool will provide a test server for us to use on our tests.

Selenium

By default, Capybara uses the :rack_test driver, which is fast but limited: it does not support JavaScript, nor it is able to access HTTP resources outside of your Rack application, such as remote APIs and OAuth services. To get around these limitations we will use Selenium driver.

Simplecov

SimpleCov is a code coverage analysis tool for Ruby. We will use this tool to make sure that most of our code is being tested.

Step by Step

1) Create your factories

To start testing our Rails app we need data. We need to create factories for each model that we have on our Rails project.

spec/factories/user.rb

FactoryBot.define do
  factory :user do
    client
    name { Faker::Name.name }
    email { Faker::Internet.email }
    phone_number { '099999999' }
    password { Faker::Internet.password }
    validated { true }
  end
end

2) Test your models

All the logic of our Rails app uses the defined models. We need to make sure that our models work as expected before testing anything else. When testing a model it's very important to check that the factory that we created for that model is valid. The next step is to test the model validations and associations. Finally we need to implement unit tests for each of the model's method.

require 'rails_helper'

RSpec.describe User, type: :model do
  it 'has a valid factory' do
    expect(build(:user)).to be_valid
  end

  describe 'associations' do
    it { is_expected.to have_many :purchases }
  end

  describe 'validations' do
    it { is_expected.to validate_presence_of(:first_name) }
    it { is_expected.to validate_presence_of(:last_name) }
    it { is_expected.to validate_presence_of(:phone_number) }
    it { is_expected.to validate_presence_of(:email) }
  end

  describe '#method' do
  end
end

3) Test your services

Implement unit tests for your services.

4) Feature tests

Feature specs are high-level tests meant to exercise slices of functionality through an application. They should drive the application only via its external interface, usually web pages.

Feature specs don't have to pay too much attention to HTML structure. They need to validate that your app features are working as expected.

For example, let's test the login feature:

require 'rails_helper'

RSpec.feature 'user login', type: :feature do
    let!(:user) { create(:user, password: 'specspec') }

    context 'with valid credentials' do
      it 'redirects logged in user to categories index' do
        visit '/users/sign_in'
        fill_in 'user_email', with: user.email
        fill_in 'user_password', with: user.password
        click_button 'Log in'

        expect(page).to have_current_path(categories_path)
      end
    end

    context 'with invalid credentials' do
      xit 'shows login error'
    end
  end
end