RSpec Verifying Doubles: Choosing instance_double, class_double, or object_double
Use RSpec verifying doubles to catch stale mocks early. Compare instance_double, class_double, and object_double with validation steps.
18 Aug 2026, 10:29 UTC

The problem: plain doubles hide stale interfaces
A test stubs a method on a mock object. The production class later removes or renames that method. Because a plain RSpec double accepts any method name, the test keeps passing. The breakage surfaces only when real code runs, often in production or in a different test that exercises the actual class.
Useful takeaway: in RSpec 3.x, prefer verifying doubles when the real class or object can be loaded. They fail fast if you stub a method the real object does not implement, turning a silent mock into an immediate test failure.
Decision and constraints
Decision: which double type to use for a given mocking target.
Constraints:
- RSpec 3.x with
rspec-mocksavailable. - The real class or object must be loadable so RSpec can inspect its interface.
- You want method-existence verification, not argument or return-type checking.
Supported options compared
| Double type | Target | Verifies | Best for |
|---|---|---|---|
instance_double | Instances of a class | That instance methods exist on the real class | Service objects, models, plain Ruby objects |
class_double | A class or module | That class methods exist on the real class | ActiveRecord class scopes, utility classes |
object_double | A specific object instance | That methods exist on that object | Objects without a stable class constant |
double (plain) | Anything | Nothing | Ad-hoc stubs where the real class is unavailable |
Trade-offs
Verifying doubles
Advantage: stubbing a missing method raises an error during test setup, so interface drift is caught immediately.
Limitation: verification covers method names only. It does not check argument counts, keyword arguments, or return types.
Risk: a legitimate refactor that renames a method will break tests that stub the old name. Treat that as a signal to update the mock, not as a reason to switch to plain doubles.
Plain doubles
Advantage: no dependency on the real class. Useful when the class is not yet defined, loads slowly, or is intentionally excluded from the test.
Limitation: any method name is accepted, so stale stubs can pass indefinitely.
Concrete implementation
Assume PaymentProcessor defines charge but not refund, and ReportFormatter defines as_csv.
# spec/lib/payment_charge_spec.rb
require 'spec_helper'
RSpec.describe PaymentCharge do
it 'stubs an existing instance method' do
processor = instance_double(PaymentProcessor)
allow(processor).to receive(:charge).with(1000).and_return(true)
result = described_class.new(processor).execute
expect(result).to be true
expect(processor).to have_received(:charge).with(1000)
end
it 'fails when stubbing a missing instance method' do
fake = instance_double(PaymentProcessor)
expect do
allow(fake).to receive(:refund)
end.to raise_error(RSpec::Mocks::MockExpectationError)
end
it 'verifies class methods with class_double' do
gateway = class_double(PaymentGateway)
allow(gateway).to receive(:process).with(1000).and_return(true)
end
it 'verifies an arbitrary object with object_double' do
formatter = object_double(ReportFormatter.new)
allow(formatter).to receive(:as_csv)
end
end
Run the spec from the project root:
bundle exec rspec spec/lib/payment_charge_spec.rb
Where to run: the application directory that contains Gemfile and spec/. Required permissions: read access to the spec and source files; no production state is changed. Expected checks: the first example passes; the second fails with a verifying-doubles error because refund is not defined on PaymentProcessor. Risk: if PaymentProcessor is not loaded when the spec runs, RSpec may raise a loading error instead of a mock verification error.
Confirm the toolchain before relying on verifying doubles:
rspec --version
grep rspec-mocks Gemfile.lock
rspec --version should report 3.x. grep rspec-mocks Gemfile.lock should list the mocking gem.
Limitations and how to verify the result
- Verifying doubles require the real class or object to be loadable. They cannot double a completely undefined class.
- They verify method existence only, not method signatures.
- After renaming a production method, run the spec suite. Specs that stub the old name on a verifying double should fail immediately.
No rollback is required because this change affects test files only.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.