liveBook · Manning

Mocking framework

As explained in The Art of Unit Testing 3e

A software framework that creates and configures test doubles and supports checking their interactions.

A mocking framework, also called an isolation framework, is a library that creates, configures, and verifies test doubles at run time. It reduces the repetitive variables, functions, classes, and boilerplate needed for handwritten mocks and stubs, while helping isolate a unit of work from its dependencies. Despite the name, it supports both mocks, which verify interactions, and stubs, which simulate returned values or errors. section 5.0 section 5.1

Use one when a dependency makes a focused test awkward: a module imported directly, a function whose calls must be tracked, or a large interface that would otherwise need a handwritten fake. Frameworks can make those tests shorter and clearer, but easy faking can also hide strong coupling to third-party modules or produce interaction-heavy tests that are difficult to trust and maintain. section 5.2 section 5.4.1 section 5.6

A framework can support several levels of dependency isolation:

  • Replace an imported module, configure its returned values, and verify calls made to it. This is useful when production code has hard module dependencies. section 5.2
  • Create a tracked function with jest.fn(), then check its calls with a matcher such as toHaveBeenCalledWith(). section 5.3
  • Build an object from individual tracked functions for an interface. Keep shared fake construction in a factory helper when several tests need it. section 5.4.1
  • Generate a type-aware fake object and verify only the relevant call, reducing changes when its interface gains functions. section 5.4.2

Testing a logger interaction

A password verifier queries a configuration service for a logging level and commands a logger to record PASS or FAIL. Jest can replace both modules with jest.mock(), set getLogLevel to return "info", run verifyPassword, and assert that logger.info received text matching PASS. The test stubs the incoming configuration value and mocks the outgoing logging interaction. section 5.2 section 5.3

Listing 5.2 Faking the module APIs directly with jest.mock()
jest.mock("./complicated-logger");                              #1
jest.mock("./configuration-service");                           #1
 
const { stringMatching } = expect;
const { verifyPassword } = require("./password-verifier");
const mockLoggerModule = require("./complicated-logger");       #2
const stubConfigModule = require("./configuration-service");    #2
 
describe("password verifier", () => {
  afterEach(jest.resetAllMocks);                                #3
 
  test('with info log level and no rules, 
          it calls the logger with PASSED', () => {
    stubConfigModule.getLogLevel.mockReturnValue("info");       #4
 
    verifyPassword("anything", []);
 
    expect(mockLoggerModule.info)                               #5
      .toHaveBeenCalledWith(stringMatching(/PASS/));            #5
  });
 
  test('with debug log level and no rules, 
        it calls the logger with PASSED', () => {
    stubConfigModule.getLogLevel.mockReturnValue("debug");      #6
 
    verifyPassword("anything", []);
 
    expect(mockLoggerModule.debug)                              #7
      .toHaveBeenCalledWith(stringMatching(/PASS/));            #7
  });
});
#1 Faking the modules
#2 Getting the fake instances of the modules
#3 Telling Jest to reset any fake module behavior between tests
#4 Configuring the stub to return a fake “info” value.
#5 Asserting that the mock was called correctly
#6 Changing the stub config
#7 Asserting on the mock logger as done previously

This listing shows module-level setup, stub configuration, mock verification, and reset cleanup in one short Arrange-Act-Assert test. section 5.2

Prefer a mock only when the important observable result is a call to a third-party dependency. Most tests can instead check a return value or state change, which assumes less about implementation details. When module faking is unavoidable in legacy code, use it deliberately; for new code, introduce abstractions over third-party modules to reduce refactoring when their APIs change. section 5.6.1 section 5.7

How The Art of Unit Testing 3e covers it

How deep it goes
Explains dynamic module, function, and interface fakes with Jest and substitute.js, then weighs maintainability traps.
Examples
Demonstrates a Password Verifier using configuration and logger modules, jest.fn() logger calls, and an IComplicatedLogger fake.
What it assumes
Introduces framework-driven fake creation after manual mocks and stubs, without requiring prior framework knowledge.
What you can do after
Apply it via Jest or substitute.js to simulate dependency behavior and verify selected interactions in JavaScript or TypeScript tests.
How it connects
Connects mocks and stubs with test doubles, Arrange-Act-Assert, command/query separation, return-value testing, and state-based testing.

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.