1
votes

I have a Jest test that I am writing for a function which makes an API call. If the API call returns a 403, a function from a node module should be called. I am trying to test this using a mock jest function, but I cannot get the test to use the mocked version of the module I am making.

file.spec.js

import file from './file'

const mockedNodeModule = jest.genMockFromModule('nodeModule')
mockedNodeModule.namedExport = { logout: jest.fn() }

it('call returns a 403', async () => {
      await file.apiCall('brand', 'entityType', 'name')
      expect(mockedNodeModule.namedExport.logout).toHaveBeenCalled()
})

file.js

import { namedExport } from './nodeModule';
import api from './api';

const apiCall = () => {
  return api.makeCall().then(
    () => {},
    (error) => {
      if (error.status === 403) {
        namedExport.logout();
      }
    },
  );
};

export default { apiCall };

The test always fails when I check whether mockedNodeModule.namedExport.logout has been called. When I put a breakpoint on the line that it is called, it seems that the mocked version is not being used when the test is running (i.e. it is still using the module from my node_modules). I have also tried using jest.mock() as well, but the result is the same. Is there something wrong in the way that I am setting up the test? Can jest not mock node modules in cases like this?

1
You're generating a mock, but not actually using it anywhere.jonrsharpe
@jonrsharpe Thanks for the response. When I use jest.mock for mocking out my own modules, those modules are automatically used when I run the test (but not if it is a node module). When I put breakpoints in my tests I can see that it is using my mocked module. Is that not the case for jest.genMockFromModule? I would expect in file.js when namedExport.logout is called, it would use the one defined in the mocked module I made using genMockFromModule.srz

1 Answers

1
votes

jest.mock(moduleName, factory, options) should work.

E.g.

file.js:

import { namedExport } from './nodeModule';
import api from './api';

const apiCall = () => {
  return api.makeCall().then(
    () => {},
    (error) => {
      if (error.status === 403) {
        namedExport.logout();
      }
    },
  );
};

export default { apiCall };

api.js:

function makeCall() {}

export default { makeCall };

nodeModule.js:

export const namedExport = {
  logout() {
    console.log('real implementation');
  },
};

file.test.js:

import file from './file';
import api from './api';
import { namedExport } from './nodeModule';

jest.mock('./nodeModule', () => {
  const mNamedExport = {
    logout: jest.fn(),
  };
  return { namedExport: mNamedExport };
});

jest.mock('./api', () => {
  return { makeCall: jest.fn() };
});

describe('59831697', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  it('should handle error if http status equal 403', async () => {
    const mError = { status: 403 };
    api.makeCall.mockRejectedValueOnce(mError);
    await file.apiCall();
    expect(namedExport.logout).toHaveBeenCalledTimes(1);
  });
});

Unit test results with coverage report:

 PASS  src/stackoverflow/59831697/file.test.js (13.506s)
  59831697
    ✓ should handle error if http status equal 403 (7ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |      100 |       50 |    66.67 |      100 |                   |
 file.js  |      100 |       50 |    66.67 |      100 |                 8 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        15.448s

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59831697