Test Spy
A test double that records information about how it was called, allowing verification of interactions after the code under test has executed.
Also known as: Spy, Test Spies
Category: Software Development
Tags: testing, software-engineering, patterns, quality
Explanation
A test spy is a type of test double that, like a stub, can provide canned responses, but additionally records information about how it was called. This recorded information, such as call counts, arguments passed, and invocation order, can be inspected after the code under test has finished executing.
Spies occupy a middle ground between stubs and mocks. Like stubs, they provide controlled responses without throwing exceptions for unexpected calls. Like mocks, they enable verification of interactions. The key difference from mocks is timing: mocks verify expectations during execution (failing immediately on unexpected calls), while spies collect information for assertions after execution.
This 'record and verify later' approach gives spies several advantages. Tests using spies are often more readable because the arrange, act, and assert phases are clearly separated. Spies also produce better failure messages, since the assertion happens in the test code itself, not inside the test double. And they are more forgiving of incidental interactions that are not relevant to the test.
A classic example from Meszaros's work is an email service spy. When testing a system that should send emails under certain conditions, a spy email service records all calls to its send method. After the code runs, the test asserts that send was called exactly once with the expected recipient and subject.
In practice, most modern testing frameworks implement spies as their primary test double mechanism. Jest's `jest.fn()`, Sinon.js's `sinon.spy()`, and Jasmine's `jasmine.createSpy()` all create spies that record calls and can optionally be configured with return values (making them stub-spy hybrids).
Spies are particularly useful for testing event handlers, callbacks, and side effects. They answer questions like 'was this function called?', 'how many times?', 'with what arguments?', and 'in what order relative to other calls?' without requiring the strict upfront specification that mocks demand.
Related Concepts
← Back to all concepts