Testing Rails Applications: From RSpec to System Tests

2 min read
Testing Rails Applications: From RSpec to System Tests

Complete Rails Testing Guide

Testing is crucial for maintaining a healthy Rails application. Let's explore modern testing practices using RSpec and system tests.

Setting Up RSpec

# Gemfile
group :development, :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'faker'
  gem 'shoulda-matchers'
end

group :test do
  gem 'capybara'
  gem 'selenium-webdriver'
  gem 'webdrivers'
end

Model Testing

# spec/models/user_spec.rb
require 'rails_helper'

RSpec.describe User, type: :model do
  describe 'validations' do
    it { should validate_presence_of(:email) }
    it { should validate_uniqueness_of(:email) }
    it { should have_secure_password }
  end

  describe 'associations' do
    it { should have_many(:posts).dependent(:destroy) }
    it { should have_many(:comments) }
  end

  describe '#full_name' do
    it 'returns the combined first and last name' do
      user = User.new(first_name: 'John', last_name: 'Doe')
      expect(user.full_name).to eq('John Doe')
    end
  end
end

Controller Testing

# spec/controllers/posts_controller_spec.rb
RSpec.describe PostsController, type: :controller do
  let(:user) { create(:user) }
  let(:post_obj) { create(:post, user: user) }

  before { sign_in user }

  describe 'GET #index' do
    it 'returns successful response' do
      get :index
      expect(response).to be_successful
    end
  end

  describe 'POST #create' do
    context 'with valid params' do
      it 'creates a new post' do
        expect {
          post :create, params: { post: attributes_for(:post) }
        }.to change(Post, :count).by(1)
      end
    end
  end
end

System Tests with Capybara

# spec/system/user_creates_post_spec.rb
require 'rails_helper'

RSpec.describe 'User creates a post', type: :system do
  let(:user) { create(:user) }

  before do
    driven_by(:selenium_chrome_headless)
    sign_in user
  end

  it 'successfully creates a new post' do
    visit new_post_path

    fill_in 'Title', with: 'My New Post'
    fill_in 'Content', with: 'This is the content'
    select 'Published', from: 'Status'

    click_button 'Create Post'

    expect(page).to have_content('Post was successfully created')
    expect(page).to have_content('My New Post')
  end
end

Testing Best Practices

  • Use factories: Create realistic test data with FactoryBot
  • Keep tests focused: One assertion per test when possible
  • Use contexts: Group related tests
  • Mock external services: Use VCR or WebMock
  • Run tests in parallel: Speed up test suite

Performance Testing

# spec/performance/posts_spec.rb
require 'rails_helper'
require 'benchmark'

RSpec.describe 'Posts performance' do
  it 'loads posts index quickly' do
    create_list(:post, 100)

    time = Benchmark.realtime do
      get posts_path
    end

    expect(time).to be < 0.5
  end
end