Interaction testing checks how a unit of work communicates with an outgoing dependency: which calls it makes and which parameters it sends. It targets the third kind of exit point—a call to a third-party function, module, object, or system—rather than only a returned value or a state change. section 4.0 section 4.10
Mocks make this boundary observable. A mock replaces an outgoing dependency and is asserted against; a stub supplies incoming data or behavior but is not verified. Stubs are waypoints toward the result, while mocks represent an exit point. section 4.1 section 4.10
Checking a logger call
A unit can receive a mock logger through a parameter: `function run(logger) { logger.info("started"); }`. The test invokes `run(loggerMock)` and verifies that `info` was called with `"started"`. The assertion checks the interaction with the dependency, not the logger's internal implementation. section 4.1 section 4.10
Use interaction testing deliberately:
- Keep the test focused on an important outgoing interaction, usually with no more than one mock. section 4.1 section 5.6.5
- Prefer stubs when you only need to provide simulated values or exceptions; do not verify calls made to them. section 5.6.5
- Inject mocks through parameters, factory or partial-application functions, module abstraction, or objects and interfaces. section 4.10
- Avoid expectations that merely record every internal call: excessive interaction assertions make tests fragile when implementation changes. section 5.6.5
Figure 4.1 On the left, an exit point that is implemented as invoking a dependency. On the right, the dependency provides indirect input or behavior and is not an exit point.
Use the figure to distinguish an outgoing call that forms an exit point from a dependency that supplies indirect input or behavior. section 4.1
