Ruby Logo

Mocking, Stubbing & Test Doubles

Use doubles, spies, dependency injection, WebMock, and VCR to isolate collaborators and external services.

Home Ruby Mocking, Stubbing & Test Doubles

Mocking, Stubbing & Test Doubles

Test doubles help isolate units, simulate external systems, and verify collaborations without hitting real services. Ruby offers flexible options that work across RSpec and Minitest.

Catalog of doubles

Mocks

Expect calls ahead of time (`expect(service).to receive(:call)`). Use sparingly; prefer spies to avoid overspecifying behaviour.

Stubs

Override method return values during the test (`allow(user).to receive(:timezone).and_return('UTC')`). Restore the original behaviour automatically after each example.

Spies

Observe interactions after the fact (`analytics = spy('Analytics'); ...; expect(analytics).to have_received(:track)`), great for verifying messaging behaviour.

RSpec syntax for doubles

RSpec.describe LessonPublisher do let(:mailer) { instance_double(LessonMailer) } let(:analytics) { class_double(AnalyticsService) } subject(:publisher) { described_class.new(mailer: mailer, analytics: analytics) } before do allow(mailer).to receive_message_chain(:published_email, :deliver_later) allow(analytics).to receive(:track) end it 'notifies and tracks the publication' do publisher.call(lesson) expect(mailer).to have_received(:published_email).with(lesson) expect(analytics).to have_received(:track).with('lesson_published', hash_including(lesson_id: lesson.id)) end end

Minitest stubs & mocks

require "test_helper" class TrialReminderJobTest < ActiveJob::TestCase test "enqueues email" do reminder = Minitest::Mock.new reminder.expect :call, true, [User] TrialReminderJob.any_instance.stub :reminder_service, reminder do assert_enqueued_with(job: ActionMailer::MailDeliveryJob) do TrialReminderJob.perform_now(users(:demo)) end end reminder.verify end end

`Minitest::Mock#expect` verifies arguments and call counts; remember to call `verify`. Use `stub` to patch methods temporarily.

External calls & HTTP mocking

  • WebMock: intercept HTTP requests (`stub_request(:post, 'https://api.example.com')`). Fail the spec if unexpected calls leak through.
  • VCR: record and replay HTTP responses. Store cassettes in `spec/fixtures/vcr_cassettes` or `test/support/cassettes`.
  • Fakes: build lightweight in-memory implementations (e.g., `FakePaymentGateway`) for fast tests while preserving behaviours relevant to the suite.

Avoid these pitfalls

  • Over-mocking locks tests to implementation. Mock only boundaries, not private methods or Ruby core APIs.
  • Stubbing time globally? Prefer `travel_to` (ActiveSupport) or `freeze_time` to keep tests deterministic.
  • Remember to clean state between examples—WebMock + VCR cassettes should reset so tests don’t leak data.

Quick Navigation

Related Topics

Video Tutorial

Watch and learn mocking, stubbing & test doubles

Pro Tip: After reading through the content above, watch this video to reinforce your understanding and see the concepts in action!