What is the difference between a mock, a stub, a fake and a spy?
Quick Answer
All are test doubles. A stub returns canned answers to calls. A mock also has expectations you verify (“was Send called once with this argument?”). A fake is a real working implementation that's just not production-suitable (an in-memory repository). A spy records calls for later inspection. A dummy is a placeholder that's never used.
They're all test doubles, differing in what they do. A stub just returns a fixed answer when you call it. A mock does that too, but you also set expectations on it and verify them — “assert that SendEmail was called exactly once.” A fake is an actual working implementation that isn't fit for production, like an in-memory list standing in for a database. A spy records what happened for you to check afterwards. My preference is to verify state — use a stub or fake and check the outcome — and only verify behaviour with a mock when the interaction itself is the thing I care about, like an email being sent, because behaviour verification makes tests fragile.
- State verification (preferred) — use stubs/fakes and assert on the result or the state afterwards.
- Behaviour verification — use mocks and assert on the interactions; useful when the effect is the interaction (an email was sent) but over-use makes tests brittle and coupled to implementation.
- Moq/NSubstitute blur the lines — a single object can act as a stub (
Setup().Returns()) and a mock (Verify()). - Prefer a real object > a fake > a stub > a mock, in that order, for robustness.