liveBook · Manning

Mocking

As explained in The Art of Unit Testing 3e

A programmable test double that replaces an outgoing dependency and records or verifies interactions with it.

A mock is a test double for an outgoing dependency: something the code under test calls to produce an effect beyond itself. Instead of using the real logger, email service, module, or third-party system, a test supplies a controllable replacement and verifies the call and its arguments. This is interaction testing: proving that an important exit point was reached correctly. section 4.1 section 4.10

Mocks and stubs are easy to confuse because both replace dependencies. The distinction is the direction of information and what the test asserts. A stub supplies incoming data or behavior and is not verified. A mock represents an outgoing requirement and is verified. When the role is unclear, “test double” or “fake” is a safer general term. section 3.1 section 3.8

Verify a logger call

Inject a logger as a parameter, then use a small handwritten mock that records what it receives. After calling verifyPassword2, check the recorded text. The mock only needs the info method used by the code, while the production logger can have a larger interface. const mockLog = { info: (text) => { written = text; } }; verifyPassword2('anything', [], mockLog); expect(written).toMatch(/PASSED/); section 4.3

Listing 4.3 Handwritten mock object
describe('password verifier with logger', () => {
    describe('when all rules pass', () => {
        it('calls the logger with PASSED', () => {
            let written = '';
            const mockLog = {
                info: (text) => {
                    written = text;
                }
            };
 
            verifyPassword2('anything', [], mockLog);
 
            expect(written).toMatch(/PASSED/);
        });
    });
});

A complete small test shows a mock recording a logger argument and verifying the observable interaction. section 4.3

Use mocks selectively to keep tests focused and trustworthy:

  • Mock a third-party call when it is the meaningful outcome to prove, such as logging or sending an email. section 4.0
  • Prefer return-value or state-based tests when they can prove the behavior; most tests need no mock. section 1.5 section 5.6.1
  • Keep one mock per test in the usual case, because each mock represents a separate requirement. section 4.1
  • Do not verify stubs or internal function calls; those checks couple a test to implementation details rather than an exit point. section 5.6.3 section 8.3.1
  • Create mocks and their expectations near the test that uses them; use a helper when shared construction is needed. section 9.4

Interaction assertions can become fragile when they verify many incidental calls. A test with many expectations may fail after harmless implementation changes, obscure the scenario being tested, and leave later verifications unexecuted after an earlier failure. Verify only the interaction that expresses the current requirement, and split unrelated requirements into separate tests. section 4.4 section 5.6.5

How The Art of Unit Testing 3e covers it

How deep it goes
Defines mocks, contrasts stubs, demonstrates injection and handwritten verification, and details overspecification and readability failures.
Examples
Demonstrates a password verifier with an injected mock logger that records and verifies a PASSED message.
What it assumes
Introduces mocking from basic unit-test exit points and distinguishes it from stubbing without assuming framework knowledge.
What you can do after
Apply it by injecting a mock dependency and verifying one required outgoing call with its arguments.
How it connects
Connects mocks to test doubles, stubs, interaction testing, dependency injection, return-value tests, and state-based tests.

Also explained in

Compare how 21 books explain mocking →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Art of Unit Testing 3e. The section links point to the parts of the book it draws on.