Doubles, Stubs, Spies & the Rails Ecosystem
Doubles & Verifying Doubles
user_double = double('User', name: 'Ada')
# verifying double — checks the stubbed method actually exists on User
user_double = instance_double(User, name: 'Ada')
allow(repo).to receive(:find).and_return(user_double)
expect(mailer).to receive(:send_welcome).with(user_double)A plain double happily stubs a nonexistent method; instance_double catches that mismatch immediately — a real safety net when the real class's interface changes.
allow + have_received (Spy Pattern)
allow(mailer).to receive(:send_welcome)
user.register!
expect(mailer).to have_received(:send_welcome).with(user)
# assert AFTER the action — reads more naturally as Arrange/Act/Assert
# than expect().to receive() set up BEFORE the action runsShared Examples
RSpec.shared_examples 'a queryable resource' do
it 'responds to #find' do
expect(subject).to respond_to(:find)
end
end
RSpec.describe UserRepository do
it_behaves_like 'a queryable resource'
endAvoids duplicating the same behavioral-contract examples across multiple classes that should all satisfy the same interface.
The Rails Testing Stack
RSpec is Rails-agnostic but dominant in the Rails community, typically paired with rspec-rails (controller/model/request test helpers), FactoryBot (test data generation), and often Capybara for feature tests. RSpec's DSL-heavy expressiveness trades some directness for readability compared to Minitest's simpler assert_equal style.
Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free