Table of Contents
Syntax gotchas
When using the module factory mocking style with jest, you need to pass the mocks themselves in as a function that calls the mock function:
hello: () => mockHello()
instead of setting them directly:
hello: mockHello
For example:
import goodbye, { hello } from './greeter'const mockGoodbye = () => 'goodbye jest'const mockHello = jest.fn()jest.mock('./greeter', () => ({__esModule: true,default: mockGoodbye,hello: mockHello}))// inside test// TypeError: greeter_1.default is not a functionexpect(goodbye()).toBeUndefined()// TypeError: greeter_1.hello is not a functionexpect(hello()).toEqual('hello jest')
Jest doesn't automock everything when using a module factory
You also need to make sure that you include an entry in the mocked module for each function your test needs to be able to call.
If you pass in the module factory parameter, jest will not automock the module and instead only set the module to the mocked definition you provide it in the factory.
Forgetting to specify the mocked function won't result in it being mocked in this case:
import goodbye, { hello } from './greeter'jest.mock('./greeter', () => ({__esModule: true,default: jest.fn(),// Omitting the definition for hello in this case won't automatically// assign it to jest.fn():// hello: jest.fn(),}))// inside testexpect(goodbye()).toBeUndefined() // correct// TypeError: greeter_1.hello is not a functionexpect(hello()).toBeUndefined()
Don't mix the module factory vs automocking strategies
Remember, when using the module factory as the second argument to jest.mock, it disables automocking for the module and the only functions that now exist for the module are what the module factory returns.
So when writing mocks using this strategy, don't try to use the
const mockHello = hello as jest.Mock<any>
strategy at the same time for mocking named exports. That strategy relies on Jest automocking everything to make it work.
const mockHello = hello as jest.Mock<any>jest.mock('./greeter', () => ({__esModule: true,default: jest.fn(),// Adding or removing this line won't fix it either:// hello: () => mockHello}))// TypeError: mockHello.mockImplementation is not a functionmockHello.mockImplementation(() => 'hello jest')
Correctly using an ordinary function as a mock implementation
You can use an ordinary function as a mocked implementation, but it's more limited than using a mock created with jest.fn()
.
You won't be able to spy on it, or set a different mock implementation partway through your tests:
const mockHello = () => 'hello jest'jest.mock('./greeter', () => ({__esModule: true,default: () => 'goodbye jest',// This will work fine,// if you only need the one implementation for all your testshello: () => mockHello()}))describe('using ordinary functions for mock implementations', () => {it('should use the mock implementation', () => {expect(hello()).toEqual('hello jest')})it('will not be able to set a new mock implementation', () => {// compile error, since mockHello is not a jest mockmockHello.mockImplementation(() => 'hi jest')expect(hello()).toEqual('hello jest')})it('will not be able to spy on it', () => {hello()// Matcher error: received value must be a mock or spy functionexpect(mockHello).toHaveBeenCalled()})})