Search code examples
typescriptunit-testingjestjsmocking

Mocking a class dependency in jest with TypeScript


I have read Mock dependency in Jest with TypeScript, Typescript and Jest: Avoiding type errors on mocked functions, and many other articles on Stack Overflow, but while I can get this code working for one test, I can't get it working for two tests.

I am using ts-jest and Postgres. I have working code to make a mock of the Pool() constructor, and the pool instance has a query() method that returns some mock data.

import { describe, expect, test } from "@jest/globals";
import { checkLimits } from "./db";
import { MINUTES } from "./constants";
const log = console.log;

// Make a mock for the pg Pool constructor
jest.mock("pg", () => {
  return {
    Pool: jest.fn(() => ({
      query: jest.fn(() => {
        return {
          rows: [
            {
              timestamps: [Date.now() - 10 * MINUTES],
            },
          ],
        };
      }),
    })),
  };
});

describe("checkLimits", () => {
  test("allows reasonable usage by address", async () => {
    await checkLimits("abcdef");
  });
});

This code works.

What I would like to do, and have attempted to do unsuccessfully, is to be able to use mockedQuery.mockReturnValueOnce() in my tests.

So I can do:

describe("checkLimits", () => {
  test("allows reasonable usage by address", async () => {
    mockedQuery.mockReturnValueOnce({
      rows: [
        {
          timestamps: [Date.now() - 10 * MINUTES],
        },
      ],
    });
    await checkLimits("abcdef");
  });

  test("also allows on more usage", async () => {
    mockedQuery.mockReturnValueOnce({
      rows: [
        {
          timestamps: [
            Date.now() - 10 * MINUTES,
            Date.now() - 3 * MINUTES,
            Date.now() - 2 * MINUTES,
          ],
        },
      ],
    });
    await checkLimits("abcdef");
  });
});

However, between JS hoisting issues, ts-jest deprecations, and other complexity, I just can't seem to do it.

How can I use mockedQuery.mockReturnValueOnce() in my mocked Postgres implementation?


Solution

  • Posting an answer to help others with the same issue. I wasn't able to get mockReturnValueOnce() working, but was able to create a variable for the mock data and set that after each test.

    If someone else posts an answer using mockReturnValueOnce() I'll accept that instead. 😊

    import { describe, expect, test } from "@jest/globals";
    import { Row, checkLimits } from "./db";
    import { Pool } from "pg";
    import { MINUTES } from "./constants";
    const log = console.log;
    
    let mockRows: Array<Row> = [];
    
    // Make a mock for the pg Pool constructor
    jest.mock("pg", () => {
      return {
        Pool: jest.fn(() => ({
          query: jest.fn(() => {
            return {
              rows: mockRows,
            };
          }),
        })),
      };
    });
    
    describe("checkLimits", () => {
      test("is fine when there's no previous usage", async () => {
        mockRows = [];
        await checkLimits("some ok value");
      });
    
      test("allows reasonable usage", async () => {
        mockRows = [...snip...];
        await checkLimits("some ok value");
      });
    
      test("blocks unreasonable usage", async () => {
        mockRows = [...snip...];
        await expect(checkLimits("some bad value")).rejects.toThrow(
          "some error"
        );
      });
    });