When referring to the definitions of Spies, Stubs and Mocks I like to return to XUnit Test Patterns:
- Test Spy: "We use a Test Double to capture the indirect output calls made to another component by the SUT for later verification by the test."
- Test Stub: "We replace a real object with a test-specific object that feeds the desired indirect inputs into the SUT."
- Mock Object: "We replace an object the SUT depends on with a test-specific object that verifies it is being used correctly by the SUT."
You may have noticed that a Spy and Mock seem to have similar definitions, and do serve similar purposes, however Mocks specify expectations up-front and don't require assertions in contrast to Spies, that verify after the fact. Here's an example that compares Rspec Mocks with Bourne's Spies:
SUT is an abbreviation for System Under Test.
describe Transfer, "using Rspec Mock" do
it "deposits amount into destination account" do
source_account = stub("source account", withdraw: 10)
destination_account = mock("destination_account")
destination_account.should_receive(:deposit).with(10)
transfer = Transfer.new(source_account, destination_account)
transfer.call
end
end
describe Transfer, "using Bourne Spy" do
it "deposits amount into destination account" do
source_account = stub("source account", withdraw: 10)
destination_account = mock("destination_account")
transfer = Transfer.new(source_account, destination_account)
transfer.call
destination_account.should have_received(:deposit).with(10)
end
end
The above patterns are collectively know as Test Doubles: "We replace a component on which the SUT depends with a test-specific equivalent." Rspec's built-in mocking library includes a double method/alias, see An Example using RSpec double, mock, and stub.
Double-Ruby (RR) is another example of a test double framework implemented in Ruby.