DEV Community

Ben Mitchinson
Ben Mitchinson

Posted on

Mocking classes in Jest / Typescript

Cross posted on my website mitchinson.dev

TL;DR

Example Test

GetANumber.test.ts mocks a class, and further customizes a class method on that mocked class for a single test.

Purpose

It was surprisingly hard to find a recently written article about the fastest way to setup Jest with some simple class mocks in Typescript. The following example demonstrates mocking a class and modifying methods of that mocked class within a single test.

Libraries like jest-ts-auto-mock are not used, cause I was unable to find a way to get them to work. Could have just been using them wrong but who knows.

I used the following methods to mock classes in an Express app while coding in test driven development.

Explanation

Comments are all over in this Example Repo, but some highlights are:

  • jest.mock ends up at the top of the file when it's transpiled.
    • Even if you put it under other things, it gets pulled to the top.
///////////////////
jest.mock("./Class", mockOne);

it("does a thing", () => {
  expect(class).toBe(one)
})

it("does another thing", () => {
  jest.mock("./Class", mockTwo);
  expect(class).toBe(two)
})
///////////////////

// is really

///////////////////
jest.mock("./Class", mockOne); // this mock is useless
jest.mock("./Class", mockTwo); // as it's immediately replaced

it("does a thing", () => {
  expect(class).toBe(one)
})

it("does another thing", () => {
  expect(class).toBe(two)
})
///////////////////

// You might as well always write `.mock` calls at the top of your file.
Enter fullscreen mode Exit fullscreen mode
  • The example mocks a class that's a default export. Named exports can also be mocked by nesting the class mockImplementation one layer deeper.
  • jest.mock('{path_to_custom_class}') will replace that paths default export in the runtime with the defined mock in __mocks__ dir defined at the same level.
  • If you'd like to modify a mock / assert things that happened to a mock, you need to import them from the "real" class.
    • Never manually import from __mocks__, get the exports of __mocks__ "through" a normal import.
import * as MockNumberGen from "./__mocks__/NumberGen";
import * as NumberGen from "./NumberGen"; // __mocks__/CVEPathData

// Give me the mock class i've defined in __mocks__/{classname} and let me
//    name it mock{ClassName}Constructor
// Give me the mock function i've defined in __mocks__/{classname}
const { default: mockNumberGenConstructor, mockGetOne } =
  // Get these things from the NumberGen class, because I know that jest
  //     has secretly replaced it with the contents of MockNumberGen
  NumberGen as unknown as typeof MockNumberGen;
// Tell typescript that these things are really coming from MockNumberGen,
//     so it should type them as such
Enter fullscreen mode Exit fullscreen mode

The full test is available here

Discussion (0)