Home > Back-end >  Jest: How to mock the methods of an esModule?
Jest: How to mock the methods of an esModule?

Time:11-24

I have a simple function that use the library jszip to zip some folders and files:

// app.ts
const runJszip = async (): Promise<void> => {
  const zip = new Jszip();

  zip.folder('folder')?.file('file.txt', 'just some text');
  zip.file('file.txt', 'just some text');

  await zip.generateAsync({ type: 'blob' });
};

I want to test it by spying the methods folder and file. I used for that the mocking partial strategy to handle the default export of this library:

// app.test.ts
import { runJszip } from './app';

const mockFile = jest.fn();
const mockFolder = jest.fn();

const mockJszip = jest.fn().mockImplementation(() => {
  return {
    folder: mockFolder,
    file: mockFile,
  };
});

jest.mock('jszip', () => {
  return jest.fn().mockImplementation(() => ({
    __esModule: true,
    default: mockJszip,
  }));
});

test('jszip', async () => {
  await runJszip();

  expect(mockFile).toHaveBeenCalledTimes(2);
  expect(mockFolder).toHaveBeenCalledTimes(1);
});

Unfortunately, it seems that I can't properly mock the folder method as you can see in the following error message:

    Message:
      zip.folder is not a function

      4 |   const zip = new Jszip();
      5 |
    > 6 |   zip.folder('folder')?.file('file.txt', 'just some text');

So does someone have an idea how I could mock and spy on this method?

Have a look at the minimal reproducible example.

CodePudding user response:

I have it working as expected by making mockFolder in @bln's response returning a mocked Jszip instance:

import { runJszip } from './app';

const mockFile = jest.fn();
let mockFolder: jest.Mock;

function mockJszip() {
  mockFolder = mockFolder ?? jest.fn(mockJszip);
  return {
    folder: mockFolder,
    file: mockFile,
    generateAsync: jest.fn(),
  };
}

jest.mock('jszip', () => {
  return {
    __esModule: true,
    default: mockJszip,
  };
});

test('jszip', async () => {
  await runJszip();

  expect(mockFile).toHaveBeenCalledTimes(2);
  expect(mockFolder).toHaveBeenCalledTimes(1);
});

https://stackblitz.com/edit/webpack-5-react-starter-ofxcmy?file=src/app.test.ts

CodePudding user response:

import { runJszip } from './app';

const mockFile = jest.fn();
const mockFolder = jest.fn();

function mockJszip() {
  return {
    folder: mockFolder,
    file: mockFile,
    generateAsync: jest.fn(),
  };
}

jest.mock('jszip', () => {
  return {
    __esModule: true,
    default: mockJszip,
  };
});

test('jszip', async () => {
  await runJszip();

  expect(mockFile).toHaveBeenCalledTimes(2);
  expect(mockFolder).toHaveBeenCalledTimes(1);
});

For the ?.file() chaining, maybe with some mockImplementation around mockJszip

  • Related