jest: Jest mock/spy returns undefined even when set up

šŸ› Bug Report

Mocking spy return does nothing

Iā€™m about to lose my mind here; it seems the entire mock system is b0rked. I copy-paste the examples from the docs, and they donā€™t work.

To Reproduce

// __mocks__/someUtil.js
export default {
  m1: jest.fn(() => 42),
}
// someUtil.test.js
import someUtil from './someUtil';

jest.mock('./someUtil')

console.log(someUtil.m1) // expected spy stuff
console.log(someUtil.m1()) // undefined šŸ¤Æ
Other variations that also do nothing
// __mocks__/someUtil.js

throw; // does throw

export default {
  m1: jest.fn(() => throw), // does NOT throw
}

// ---

export default {
  m1: jest.fn().mockImplementation(() => 42),
}

// ---

export default {
  m1: jest.fn().mockImplementation(() => throw), // does NOT throw
}

// ---

export default {
  m1: jest.fn().mockReturnValue(42),
}

// ---

export default () => ({
  m1: jest.fn().mockImplementation(() => 42),
})

// ---

export default () => ({
  m1: jest.fn().mockReturnValue(42),
})

Expected behavior

As documented: it should return 42

envinfo

jest version: 24.7.1

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 28
  • Comments: 28

Commits related to this issue

Most upvoted comments

A little late here, but I was just having this exact issue. I discovered that someone had added resetMocks: true to the jest.config.js file. This means that the implementations of mock functions are reset before each test. So in our case, the mock function was being included in the mocked module at test runtime, but that mock had been reset, so it returned undefined.

Regarding the original issue build environment, it looks like react-scripts does indeed add resetMocks: true into the jest config. (https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/utils/createJestConfig.js#L69) But you can override it on the jest key of your package.json. (https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/utils/createJestConfig.js#L74)

resetMocks: true was the culprite for me too.

mock implementations can work with resetMocks: true if you setup the mocks in beforeEach, or directly inside the test/it callbacks.

If you set up the mocks at the top level of the module, in the describe callback (but outside of any it/test callback), or in beforeAll, they get overwritten by resetMocks AFAICT

@mateuszs Yes: I stopped using Jest. I could not figure out what the problem was; it quite possibly was coming from create-react-app, but Iā€™d already sank far too many hours trying to get an out-of-the-box feature to work šŸ˜¦

This also caught me totally by surprise. Weā€™ve been using restoreMocks in our jest.config.js to ensure all mocked spies are reverted to their original implementation between tests.

But I would have never have expected the restoration of mock functions to restore their original implementations to affect mock functions created on-the-fly with jest.fn() as well. Is there actually a use-case for resetting jest.fn(() => 42) to jest.fn()??

I know (now) itā€™s documented: image

And itā€™s probably consistent as both jest.fn(() => 42) and jest.spyOn(obj, 'method').mockReturnValue(42) add mock implementations. But Iā€™m sure it will keep tripping people up.

@dstapleton92 Thank you so much, I sank a ton of time into google trying to find this answer!

For me it was jest.resetAllMocks();!

Had a similar issue where I couldnā€™t figure out why the import I was trying to mock was ending up undefined. I was trying

const mockMyImport = jest.fn()

jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  myImport: mockMyImport,
}));

describe(`MyComponent test`, () => {
  it('works', async () => {
    mockMyImport.mockImplementation(() => {
      console.log("in mocked implementation")
      return 42
    })
    const { toJSON } = render(<MyComponent />)
    expect(toJSON()).toMatchSnapshot();
  });
});

but in my component, logging myImport would show that it was always undefined. This, however, works:

import { myImport } from 'my-module'

jest.mock('my-module', () => ({
  ...jest.requireActual('my-module'),
  myImport: jest.fn(),
}));

describe(`MyComponent test`, () => {
  it('works', async () => {
    (myImport as jest.Mock).mockImplementation(() => {
      console.log("in mocked implementation")
      return 42
    })
    const { toJSON } = render(<MyComponent />)
    expect(toJSON()).toMatchSnapshot();
  });
});

So in other words, you have to access the mocked import by actually importing it in the test rather than using the variable that you assigned the import to be mocked byā€¦

None of the above worked and in my case the problem was that I was adding a mock in a __mocks__ directory next to the file, but the import used a ā€˜scoped moduleā€™. I had to add a folder for the scoped module under the root mocks folder ie __mocks__/@module/file.js.

The documentation mentions it.

I had the same problem in my React project. Putting resetMocks: false into package.json did not fix it. I know that jest.mock is not broken but I canā€™t get the simple examples to work either. The weird thing is that when I use VSS interactive debugger, it shows the dummyPromise implementation is my mocked implementation, but there is no evidence of mocked function support. _isMockFunction is undefined.

I have found that I can implement the supposed jest.mock behavior by using jest.spyon, mockImplementation, and importing from mocks directory.

Broken Code

import {dummyPromise} from '../services/DummyService';

jest.mock('../services/DummyService');

describe('grasping at straws why mocking service promises is not working', ()=>{
    beforeEach(()=>{
        //jest.mock('../services/DummyService.js'); // still does not work
    })

    test('can get a mock', ()=>{
        //jest.mock('../services/DummyService.js'); // still does not work
        expect(jest.isMockFunction(dummyPromise)).toBeTruthy();
    })
})

As you can see, putting the jest.mock() into beforeEach or in the test has no change in behavior.

Working Code

/* eslint-disable jest/no-mocks-import */
import * as DummyService from '../services/DummyService';
import * as DummyServiceMocks from '../services/__mocks__/DummyService';

const mockDummyPromise = jest.spyOn(DummyService, 'dummyPromise');
mockDummyPromise.mockImplementation(DummyServiceMocks.dummyPromise);

describe('grasping at straws why mocking service promises is not working', ()=>{

    test('can get a mock', ()=>{
        expect(jest.isMockFunction(mockDummyPromise)).toBeTruthy();
    })
    
    test('can call the mock', async ()=>{
        expect(jest.isMockFunction(mockDummyPromise)).toBeTruthy();
        mockDummyPromise().then(data=>{
            expect(data).not.toBeNull();
            expect(data).toEqual('dummy dummy dummy');
        })
    })
})

Yes, esLint complains about the import from __mocks__ and also thinks that the mockDummyPromise isnā€™t really a promise, but this code works.

services/DummyService.js

export const dummyPromise = () =>{
    return new Promise((resolve)=>{
        resolve('real dummy real dummy');
    });
}

services/mocks/DummyService.js

export const dummyPromise = () =>{
    return new Promise( resolve => resolve('dummy dummy dummy'));
}

resetMocks: true was the culprite for me too.

mock implementations can work with resetMocks: true if you setup the mocks in beforeEach, or directly inside the test/it callbacks.

If you set up the mocks at the top level of the module, in the describe callback (but outside of any it/test callback), or in beforeAll, they get overwritten by resetMocks AFAICT

@HenryCharlesAnderson thanks for providing this information. I tried putting jest.mock(...) inside the it callback function and itā€™s still not working. I tried putting it under beforeEach() function and that didnā€™t work either. The only way I could get it working is to set resetMocks to false in package.json. Any ideas why this is the case?

Hereā€™s some sample code Iā€™ve written to test this for more context. In this example, jest.resetMocks in package.json is set to false and it works. The moment I set it to true, this fails. As mentioned above, moving jest.mock() block into the callback function ofit() and beforeEach() functions do not work.

// simplefn.ts
const simplefn = (message: string): string => message;
export default simplefn;
// simplefn.test.ts
import simplefn from '../simplefn';

jest.mock('../simplefn', () => ({
  __esModule: true,
  default: jest.fn((msg) => 'hello'),
}));

beforeEach(() => {
  jest.restoreAllMocks();  // Contrary to my expectation, this line does not cause jest.mock() to become reset
});

describe('test simplefn', () => {
  it('works with jest.fn', () => {
    const result = simplefn('test');
    expect(result).toBe('hello');
    expect(simplefn).toHaveBeenCalledTimes(1);
  });
});

In my case I wanted to mock a value that got evaluated upon loading the file:

export const store = createStore(...);

This was always returning undefined, no resetMocks setting.

Instead I had to do:

export const store = () => {
  return createStore(...);
}