Verified doubles in RSpec: choosing between allow, expect, and spies without brittle mocks
26.5K reputation · 04 Feb 2025, 12:02 UTC
Deciding on a mocking strategy for a growing RSpec suite
Our team is standardizing how we isolate collaborators in unit tests, and we keep hitting the same unresolved decision: when to use allow, when to use expect, and when spies are the better fit. The suite mixes all three styles inconsistently, and reviewers disagree on what each test is actually promising.
We also want to rely on verified doubles (instance_double) so that stubbed methods that no longer exist on the real class fail loudly instead of silently passing. What is unclear is how strict that verification is in practice — for example, whether it also checks argument arity, and what happens when the doubled class is not loaded in the test environment.
Assume a current RSpec 3.x setup with verify_partial_doubles enabled. We have not yet measured how much of the existing suite would break under a stricter policy.
Specifically:
- Is there a documented or community-accepted rule for when a message expectation (
expect(...).to receive) is preferable to a plain stub (allow)? - Does
instance_doublevalidate method arguments as well as method names, and does it require the real class to be loadable? - Are there known downsides to converting existing generic
doubleusage to verified doubles wholesale?
1 answer
1 question comment
Use comments to ask for clarification. Post a solution as an answer.
1,840 reputation · 04 Feb 2025, 22:21 UTC
When you use instance_double (or class_double) with allow, expect, or a spy, RSpec checks not only that the method exists but also that the number and kind of arguments match the real method’s signature. This includes positional arguments and keyword arguments: if the real method expects def foo(bar:, baz:), stubbing it with allow(foo).to receive(:foo).with(bar: 1, qux: 2) will raise an ArgumentError because :qux is not a valid keyword. Default values are not validated, so omitting an optional keyword is allowed.
You can verify this behavior by intentionally mismatching keywords in a spec and observing the verification error before the test runs.