22

I have a simple suite of tests where in some cases I want to mock a module and in some cases not. However, jest.mock() works only if it's placed outside tests. Anyone has an idea why is that and what I'm doing wrong?

This is the actual import of a function I want to mock

import {hasSupport, getCallingCode} from 'utils/countryCallingCode';

And this is the mock of this function:

jest.mock('utils/countryCallingCode', () => ({
    getCallingCode: () => '1',
    hasSupport: () => true,
  }));

Now, the working scenario is:

//imports
//mock

describe('...', () -> {
    it('...', () -> {

    });
});

This DOESN'T work:

//imports

describe('...', () -> {
    //mock

    it('...', () -> {

    });
});

This also DOESN'T work:

//imports

describe('...', () -> {
    it('...', () -> {
        //mock
    });
});
k-wasilewski
  • 3,943
  • 4
  • 12
  • 29

4 Answers4

6

Jest automatically hoists jest.mock calls in ES modules to the top of the file, before any import statements. This is done in order to allow the use of the mocked module by other modules in the tree.

The Jest documentation also provides an example repository to explain how Jest mocking works.

If you want to prevent this automatic behaviour, you can use jest.dontMock.

Dean James
  • 2,491
  • 4
  • 21
  • 29
  • Doesn't work for me, I've tried it... – k-wasilewski Jan 03 '22 at 14:07
  • 2
    You probably need to set up a reproducible sandbox example if the documentation is not doing what it says it should be doing for you. – Dean James Jan 03 '22 at 14:21
  • 1
    Your link: "Caution: jest.mock calls cannot be hoisted to the top of the module if you enabled ECMAScript modules support. The ESM module loader always evaluates the static imports before executing code..." - Opposite of what you wrote but - it's probably because they added this caution after your answer – Eggcellentos Jul 05 '23 at 12:32
  • Marked down as this answer doesn't give a working example on how to mock inside describe or test functions. It's also not clear if it's possible or not, so not helpful. – Casey Gibson Aug 23 '23 at 04:36
2

I've found using jest.spyOn inside the "it" block is a workaround that solves this issue and helpful when one wants to have different mocks per test.

1

This worked for me: How to change the behaviour of a mocked import?

Mocking modules (might be also useful): jest.mock(..) not working in 'describe' (TypeError: moduleName.split is not a function)

alexfrize
  • 1,022
  • 1
  • 16
  • 35
  • Thanks for posting this man! Works flawlessly. I saw that pattern once and didn't try it yet. But it's definitely the best one. – KeitelDOG Mar 19 '23 at 22:56
1

Use mockReturnValue(...)

import {
  getCallingCode,
  hasSupport,
} from 'utils/countryCallingCode'

jest.mock('utils/countryCallingCode', () => ({
  getCallingCode: jest.fn(),
  hasSupport: jest.fn(),
}))

describe('...', () => {
    it('...', () => {
        getCallingCode.mockReturnValue(1)
        hasSupport.mockReturnValue(false)

        expect(...
    })

    it('...', () => {
        getCallingCode.mockReturnValue(0)
        hasSupport.mockReturnValue(true)

        expect(...
    })
})

Dealing with default export from that util:

import theUtil from 'utils/theUtil'

jest.mock('utils/theUtil', () => ({
  __esModule: true,
  default: jest.fn(),
}))

describe('...', () => {
    it('...', () => {
        theUtil.mockReturnValue('some value')

        expect(...
    })

    it('...', () => {
        theUtil.mockReturnValue('some other value')

        expect(...
    })
})

Typescript:

Use as jest.Mock. eg:

...
(getCallingCode as jest.Mock).mockReturnValue(1)
...
(theUtil as jest.Mock).mockReturnValue('some value')
...

or, more cleanly:

import theUtil from 'utils/theUtil'

jest.mock('utils/theUtil', () => ({
  __esModule: true,
  default: jest.fn(),
}))
const mockTheUtil = theUtil as jest.Mock

describe('...', () => {
    it('...', () => {
        mockTheUtil.mockReturnValue('some value')

        expect(...
    })
})
Matt Rabe
  • 1,753
  • 19
  • 27