Advanced RSpec testing with shared examples

Sarah Mitchell Feb 2026
3 tabs
require 'rails_helper'

RSpec.describe User, type: :model do
  subject(:user) { build(:user) }

  describe 'validations' do
    it { should validate_presence_of(:email) }
    it { should validate_uniqueness_of(:email).case_insensitive }
    it { should validate_presence_of(:name) }

    context 'when email format is invalid' do
      before { user.email = 'invalid-email' }

      it 'is invalid' do
        expect(user).not_to be_valid
        expect(user.errors[:email]).to include('is invalid')
      end
    end
  end

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

  describe '#full_name' do
    let(:user) { build(:user, first_name: 'John', last_name: 'Doe') }

    it 'returns the concatenated first and last name' do
      expect(user.full_name).to eq('John Doe')
    end

    context 'when last name is missing' do
      let(:user) { build(:user, first_name: 'John', last_name: nil) }

      it 'returns only the first name' do
        expect(user.full_name).to eq('John')
      end
    end
  end

  describe '#activate!' do
    it 'changes status to active' do
      expect { user.activate! }
        .to change(user, :status).from('pending').to('active')
    end

    it 'sets activated_at timestamp' do
      freeze_time do
        user.activate!
        expect(user.activated_at).to eq(Time.current)
      end
    end

    it 'sends activation email' do
      expect(UserMailer).to receive(:activation_email)
        .with(user).and_call_original

      expect { user.activate! }
        .to have_enqueued_mail(UserMailer, :activation_email)
    end
  end

  describe 'scopes' do
    let!(:active_user) { create(:user, status: 'active') }
    let!(:inactive_user) { create(:user, status: 'inactive') }

    describe '.active' do
      it 'returns only active users' do
        expect(User.active).to contain_exactly(active_user)
      end
    end
  end
end
3 files · ruby Explain with highlit

RSpec provides powerful testing tools for behavior-driven development. Shared examples reduce duplication across similar specs—I extract common behavior into reusable examples. Contexts organize tests by different scenarios. Let blocks lazily evaluate test data, improving performance. Subject defines the object under test. Before hooks set up test state; after hooks clean up. I use described_class for class references, enabling easy refactoring. Mocking and stubbing with double, allow, and expect isolate unit tests. Custom matchers improve test readability. Aggregate failures show all failures, not just the first. RSpec's expressiveness makes tests serve as living documentation. Well-structured specs ensure confidence in refactoring and feature additions.