jest.clearAllMocks(); does not remove mock implementation within `afterEach`

Created on 10 Oct 2018  ·  44Comments  ·  Source: facebook/jest

🐛 Bug Report

jest.clearAllMocks(); does not remove mock implementation within afterEach

To Reproduce

I have a file called src/layouts/index.js

// ./src/layouts/index.js
const importAll = (r) =>
    r.keys().reduce(
        (acc, key) => ({
            ...acc,
            [key.replace(/^\.\/(.*).json$/, '$1')]: r(key)
        }),
        {}
    );

module.exports = importAll(require.context('./', true, /\.json$/));

It utilizes webpack require.context so I am trying to mock with jest.mock.

I have another file... say file util.js

//./src/util.js
import layouts from '../layouts';
export const getLayout(name) {
  return layouts[name];
}

in my test I'm trying to clear the mocks after each test

//./src/util.test.js
describe('my test suite', () => {
  afterEach(() => {
     jest.clearAllMocks();
  })
  test('test number one', () => {
      jest.mock('./layouts', () => ({
          layout1 : { a : 1 },
          layout2 : { b: 2 },
     }));
     assert.equals(getLayout('layout1').a, 1);
     assert.equals(getLayout('layout2').b, 2);
  });
  test('test number two', () => {
     assert.equals(getLayout('layout1').a, 1);
     assert.equals(getLayout('layout2').b, 2);
  });
});

Expected behavior

I would expect for the first test to pass and the second test to fail... because the mock should have been cleared.

Link to repl or repo (highly encouraged)

https://repl.it/@CharlieHoover/SorrowfulBackSandboxes

Most helpful comment

FYI The mocking documentation and API is extremely unclear, and overly complicated IMHO.

All 44 comments

As I understand the parallel execution model of jest the tests inside each suite are run sequentially so you should be able to mock per individual test.

FYI The mocking documentation and API is extremely unclear, and overly complicated IMHO.

jest.clearAllMocks does not remove mock implementations by design - try jest.resetAllMocks

Here are the relevant docs:


Still does not work with resetAllMocks:

Example included:

https://repl.it/@CharlieHoover/SorrowfulBackSandboxes-2

Aside from that that is extremely ambiguous. Why would a function called clearAllMocks not clear the mocks... Name the function resetMockState or something more descriptive. clearAllMocks implies the mocks are being cleared.

@rickhanlonii my issue is not yet answered. I want to remove the mocks.

Ah, yeah, looks like resetAllMocks does not reset mock _module factories_ just the implementations set by mockImplementation. If you want to post what you want to do to stackoverflow I can help you do what you want there but it doesn't look like there's a bug here

Why would a function called clearAllMocks not clear the mocks

I think the confusion is that the "mock" in "clearAllMocks" does not refer to the mock implementations, it refers to the Jest mock objects. So this function means "clear out all jest mock objects" which is to say call .mockClear on all mock objects (i.e. clear the calls)

@rickhanlonii

omg so #1 it seems like "clear" and "reset" are being used opposite to what their logical meaning is.

Also, it's very clear what he's trying to do; remove the mock implementation, and you're saying there's no way to do that orrr.....?????

This should be reopened

+1

+1

+1

I have a similar issue, when I mock an implementation in previous it case, the next it case will be affected.

+1

also struggling with this!

+1 please update the docs to explain how to REMOVE a mock/spy

+1 please update the docs to explain how to REMOVE a mock/spy

Isn't this what mockRestore is for? https://jestjs.io/docs/en/mock-function-api#mockfnmockrestore

I think the default config should include:

{
  restoreMocks: true,
  clearMocks: true,
  resetMocks: true
} 

It is shocking that the default behaviour is to vomit state between tests. Can you please just keep my tests isolated by default? Then the [hopeful minority] who want to spread state across multiple tests can do so by opt-in.

`
describe('test', () => {
beforeEach(() => {
const WelcomeService = require('./../SOME_MODULE')
WelcomeServiceSpyOfMessage = jest.spyOn(
WelcomeService,
'message', // some function I mocked
)
const IsUserAuthentic = require('./../SOME_MODULE')
IsUserAuthenticSpyOnIsUserAuthentic = jest.spyOn(
IsUserAuthentic,
'isUserAuthentic' // some function I mocked
)
app = require('../src/server') // my Express server
})

afterEach(() => {
  jest.restoreAllMocks()
})

it('1. Mock implementation', async () => {
  const mockedMessage = faker.lorem.sentence()
  WelcomeServiceSpyOfMessage.mockImplementation(() => mockedMessage)
  IsUserAuthenticSpyOnIsUserAuthentic.mockImplementation(() => {
    console.log('>>> MOCKED MW 1')
    return true
  })
  const result = await request(app)
    .get('/api')
  expect(result.statusCode).toBe(200)
  expect(result.body).toHaveProperty('message', mockedMessage)
})

it('2. After restored implementation', async () => {
  IsUserAuthenticSpyOnIsUserAuthentic.mockImplementation(() => {
    console.log('>>> MOCKED MW 2')
    return true
  })
  const result = await request(app)
    .get('/api')
  expect(result.statusCode).toBe(200)
  expect(result.body).toHaveProperty('message', 'hello world')
})

})
`
Output:
console.log test/routes.test.js:36

MOCKED MW 1

console.log test/routes.test.js:36

MOCKED MW 1

Same mocked version of function is called for both the tests. Although I have restored all mocks in afterEach call, still same mock is getting called. Please tell me where I missed.

Thanks

+1 🥂

+1

+1
It seems to me that clearing the mocks after each test should be the default behavior.

+1
It seems to me that clearing the mocks after each test should be the default behavior.

+1

functions mocked with .spyOn() can be restored: jest.spyOn(object, method).mockImplementation(mockFunction).

I agree that mocks should be cleared automatically between tests, though.

+1

+1

Is there a fix for this issue? +1

Facing the same issue!

I have same issue but I not found solution from jest official documentation.
So I make a not graceful solution below. Then use it in everywhere that need to remove mock.

// jestUtil.js
const mockArr = [];
export function doAMock(moduleName, cb = null) {
  if (!mockArr.includes(moduleName)) {
    mockArr.push(moduleName);
  }
  if (cb) {
    jest.doMock(moduleName, cb);
  } else {
    jest.doMock(moduleName);

  }
}

export function removeAllMock() {
  mockArr.forEach(m => {
    jest.dontMock(m);
  });
  mockArr.length = 0;
}


// usage
// other.test.js
import * as jsUtil from './jestUtil.js';
jsUtil.doAMock('module');
jsUtil.removeAllMock();

IMO this is a super confusing API naming and deserves to be renamed.

IMO this is a super confusing API naming and deserves to be renamed.

yeah, totally agree - but I'm still wondering if this is currently possible - naming aside. If yes, how?

This should really be fixed. No reason one unit test should affect the other. Has anyone found a viable workaround? Really odd functionality.

EDIT: Solution by @damien-roche to update jest.config.js worked well, plus adding my Class.mockImplementation call in a beforeEach hook:

beforeEach(() => {
        CommunicationApi.mockImplementation(() => {
          return {
            getMessagePosition() {
              return new Promise(resolve => {
                resolve({
                  ok: true,
                  Id: '123',
                  json: function () {
                    return null
                  }
                });
              });
            },
            getMessageOptions() {
              return Promise.resolve(expectedMessageOptions);
            },
          };
        });
        getters = createGetters(baseGetters);
        wrapper = createWrapper(getters, actions)
      });

Is anyone ever going to do something about this? I am also facing the same issue.

This is still a problem

I'm also having an issue with this.

For anyone running into this, I've set the command line flag --restoreMocks which can also be set in jest.config.js:

   restoreMocks: true,

As the default for my tests so that mocks are always restored each after test run. Here is the documentation link: https://jestjs.io/docs/en/configuration#restoremocks-boolean

Agreed it is a strange default that keeps state between unit tests.

@ewhauser neither the command line flag nor the config-option is working for me. once I mock a module, it stays mocked till I overwrite the mock :(

are you sure it is working for you?

@pkyeck It seems to depend on the environment. If I run the test suite in my local environment it clears all the mocks, but when I run it inside a docker container it doesn't seem to reset them properly.

I just came across mockRestore, which works if you are using spyOn.

Does everything that mockFn.mockReset() does, and also restores the original (non-mocked) implementation.

This is useful when you want to mock functions in certain test cases and restore the original implementation in others.

Beware that mockFn.mockRestore only works when the mock was created with jest.spyOn. Thus you have to take care of restoration yourself when manually assigning jest.fn().

The restoreMocks configuration option is available to restore mocks automatically between tests.

https://jestjs.io/docs/en/mock-function-api#mockfnmockrestore

I found this helpful. Call this function with an await every time right after a mocked fn is expected to be called.

flushAllPromises() { return new Promise(resolve => setImmediate(resolve)); }

This should make the last call to YourClass.yourFunction.mockResolvedValue() in the executing test case to work, instead of relying on the resolved value of the last executed unit test.

Oh wait, I see what is going on. You change the mock implementation, but don't change any other parameters that would make running the test unique, so Jest uses the previous cached response. So long as you change some little piece of state that makes the test unique, it will pick up the changed implementation. That's why the promise flushing was suggested up above.

These APIs need to be renamed. Settling for bad naming due to inertia is unprofessional to say the least.

Come on guys, this is sheer incompetence. I don't use Jest out of choice, I use it because you have made it popular. This issue has been open since OCT 2018. You're just doing a disservice to the community you're trying to serve..? Get it together.

These APIs need to be renamed. Settling for bad naming due to inertia is unprofessional to say the least.

I agree that it is 'unprofessional', I and all others who have maintained Jest in the last ~2 years have done it in their free time, thus not professionally. I'd stop short of 'incompetence' though.
Either way, please do not use the issue tracker for off-topic comments or (successful or unsuccessful) abuse; such comments will be hidden or reported.

For me, jest.restoreAllMocks(); helped finally clear the spy on jest

Was this page helpful?
0 / 5 - 0 ratings