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

šŸ› 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

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 6
  • Comments: 105 (4 by maintainers)

Commits related to this issue

Most upvoted comments

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:

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.

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

This should be reopened

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

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…???

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.

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

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

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

This is still a problem

@SimenB would you kindly triage this for us? It’s a very old issue that could be either a serious problem or just a documentation task. It remains untagged with no consensus on what it really is.

But even this default config does not work reliably 😦 How is facebook working with such a broken test framework?

Until we get this issue tagged so it becomes reachable, it will remain a mystery whether or not it’s actually bugged or there’s a large misunderstanding from lack of documentation. People only end up here because of search engine results.

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

+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

` 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

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

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

@SimenB Hi, could you add some labels to this issue? It’s a pretty hot topic and is indexed on google, but it seems like it is outside of the radar of those who can assist with this since it is not tagged with anything.

restoreAllMocks

this worked for me

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

Anyone looking for a solution in 2021 , this fixed the issue for me 😃

Are any of the maintainers paying attention? Tests need to run in total isolation otherwise they are worthless.

has anyone found a fix for this ?

I am passing jest.clearAllMocks resetAllMocks under beforeEach and it definitely is still not clearing the mockImplementation

All other mock frameworks I can remember using across languages hook into the test framework to automatically reset all test doubles’ state to the pristine declared form after each individual example run.

Any other behavior results in unexpected non-deterministic and order-dependent behavior when running multiple examples in the same suite.

And in this case, there doesn’t even seem to be a way to manually achieve the desired behavior.

It’s confusing and mystifying why Jest wouldn’t just reset everything after each example by default.

my function is still being mocked after calling:


    jest.restoreAllMocks()
    jest.resetAllMocks()
    jest.clearAllMocks()

am i missing something?

@JRRS1982 try doing inside some it something like this:

it('mock something', () => {
   jest.mock('someModule', () => 'fake value')
})

you will see that from that moment on, all the tests that are running after this test, will get ā€˜fake-value’ if they try to import someModule. that means that you have a leak between tests. if your other tests will count on that return value, and you’ll ever remove this test, all the other tests will fail. and there are more serious issues that leaks like this can cause.

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 have mocked a module in my jest setup like this jest.mock('containers/PatientsList/SignalRConnection')

you can unmock it by using jest.unmock("containers/PatientsList/SignalRConnection") It didn’t work in beforeEach and beforeAll, so I had to put it above describe and it worked

Got a nasty issue where tests work with .only but fail in sequence. Tried the clear/reset/restore triad with all sorts of configurations described in this issue page and still no results. Worst part is, I have whole suites that have been working just fine with the same setup/teardown.

Unit tests should be isolated from each other on principle, it’s a tremendous problem for Jest to not have it. Who knows how many unit tests are out there with strange behavior inherited by this.

With the info on this page, now I can’t even tell if Jest ā€œunitā€ tests are even to be trusted because this teardown seems so impossible to gauge. It’s that huge.

has anyone found a fix for this ?

I am passing jest.clearAllMocks resetAllMocks under beforeEach and it definitely is still not clearing the mockImplementation

@SidKhanna296 jest.restoreAllMocks() is the one you’re looking for.

I have no idea why this makes the difference, but I managed to work around this by swapping

afterEach(() => {
     jest.clearAllMocks();
  })

for

beforeEach(() => {
    jest.clearAllMocks();
});

I assumed they would achieve the same result - make sure the state of the mock at the end of one test doesn’t impact the next - but apparently not 🤷

@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.

Facing the same issue!

For me the problem was that I had a static property. And the problem happened since they aren’t part of the instance and are remembered. I have changed it to a normal class property. and it worked (clearAllMocks), maybe someone else didn’t pay attention to this and had the same issue 😊

same here, is there a work around for static properties as we shouldn’t have to change our implementation to pass a unit test. Atleast, I never do.

For me the problem was that I had a static property. And the problem happened since they aren’t part of the instance and are remembered. I have changed it to a normal class property. and it worked (clearAllMocks), maybe someone else didn’t pay attention to this and had the same issue 😊

has anyone found a fix for this ? I am passing jest.clearAllMocks resetAllMocks under beforeEach and it definitely is still not clearing the mockImplementation

@SidKhanna296 jest.restoreAllMocks() is the one you’re looking for.

damn, I’ve just struggled too much trying to get why clear or reset mocks don’t actually CLEAR and RESET mocks, thank you!!! you are my savior

Interested in hearing alternatives, if restore, clear, reset cause flakiness, and reset,restore is not complete what is the correct setup?

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

I haven’t been able to find a working way of doing any of those combinations, unfortunately.

I can confirm @Niryo comment. I have 3 tests, one of which needs to mock axios. If I put that test first, all my other tests fail (they use axios but don’t need to mock it for that use case), and if I put it last, when I actually mock the API call they all pass.

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

Anyone looking for a solution in 2021 , this fixed the issue for me 😃

As simple as that šŸ˜„ @XxAmrIbrahimxX

For what its worth I found that clearAllMocks stopped working properly after an upgrade of our app. replaced is with this and things seem to pass:

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

@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?

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 please update the docs to explain how to REMOVE a mock/spy

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)

For me it worked in the end by doing this:

let wrapper;

beforeEach(() => {
    wrapper = null;
    jest.clearAllMocks();
})

it('makes bar out of foo', async () => {
    const mockMethod = jest
      .spyOn(Class, 'methodName')
      .mockImplementation();

    wrapper = someActionYouWantToPerform('foo');
    await flushPromises(); // depends on your implementation

    expect(mockMethod).toHaveBeenCalledWith('foo');
});

@agilgur5 for me jest.restoreAllMocks() is working fine when it’s called from within afterEach(). Furthermore I used mockReturnValueOnce() and mockResolvedValueOnce. Maybe this helps?

Still can’t find a solution.

I’m testing a class instance and I need to mock one of the class functions that is called by another other function in the same class.

I tried all the ā€œcleanā€ methods, even together, in the file (before, after) and in the configs.

Nothing worked.

If I change the order of the tests (so, I first test the function (A) and then I test the other function (B) that uses function A and it works.

Not sure what is wrong with it and how to fix it.

to get around the issue, here’s a pattern that works for and makes sense to me

the issue for me was resetting my mocks to those which are declared in mocks directories. The easiest solution I saw was to reset modules and re-require them before each test. This way resetAllMocks didn’t wipe out all the mocks I wanted persisted. Essentially only the one-off mocks I created in the tests are reset.

  • the example is in typescript in case anyone has trouble figuring out the syntax there. Using require instead of dynamic import gets around typing nonsense

let’s assume I mock fs.stat to return a particular object

// /__mocks__/fs.ts

// here we declare mocks we want persisted
const mockFs = {
  stat = jest.fn(() => ({ size: 100, blocks: 2, ... })
}

export default mockFs

and depend on that mock to test ./do-something.ts

// /do-something.test.ts

// declare 'let' for each dependency
let fs: jest.MockedObject<typeof import('fs')>
let doSomething: typeof import('./do-something')

beforeEach(() => {
  jest.resetAllMocks()
  jest.resetModules()
  
  fs = require('fs').default
  doSomething = jest.requireActual('./do-something').default
})

describe('some block', () => {
  it('should return a 404', () => {
    fs.stat.mockImplementation(() => throw new Error('file not found'))

    doSomething() // will have the mock implementation above
  })
  
  it('should work', () => {
    doSomething() // will have the mock implementation from /__mocks__/fs.ts
  })
})

I think if you used clearAllMocks together with restoreAllMocks you wouldn’t need to re-require the dependencies. I may be wrong though, should be tested.

to get around the issue, here’s a pattern that works for and makes sense to me

the issue for me was resetting my mocks to those which are declared in __mocks__ directories. The easiest solution I saw was to reset modules and re-require them before each test. This way resetAllMocks didn’t wipe out all the mocks I wanted persisted. Essentially only the one-off mocks I created in the tests are reset.

* the example is in typescript in case anyone has trouble figuring out the syntax there. Using require instead of dynamic import gets around typing nonsense

let’s assume I mock fs.stat to return a particular object

// /__mocks__/fs.ts

// here we declare mocks we want persisted
const mockFs = {
  stat = jest.fn(() => ({ size: 100, blocks: 2, ... })
}

export default mockFs

and depend on that mock to test ./do-something.ts

// /do-something.test.ts

// declare 'let' for each dependency
let fs: jest.MockedObject<typeof import('fs')>
let doSomething: typeof import('./do-something')

beforeEach(() => {
  jest.resetAllMocks()
  jest.resetModules()
  
  fs = require('fs').default
  doSomething = jest.requireActual('./do-something').default
})

describe('some block', () => {
  it('should return a 404', () => {
    fs.stat.mockImplementation(() => throw new Error('file not found'))

    doSomething() // will have the mock implementation above
  })
  
  it('should work', () => {
    doSomething() // will have the mock implementation from /__mocks__/fs.ts
  })
})

Shouldn’t the clearAllMocks and restoreAllMocks combo work for any use case?

clearAllMocks clears all mock calls restoreAllMocks restores all mocked implementations to their default (non-mocked) state

The way I see it, the resetAllMocks still keeps mocked implementations as mocks, only without return values or defined implementation.

If I’m wrong here, anyone please correct me

I tried restoreAllMocks and all the other restores, resets, and clears and none of them worked for me.

@JRRS1982 i am using resetModules and resetMocks. It’s not enough in terms of assuring isolation but at least it’s not flaky

@amirensit I’m using this now:

beforeEach(async () => {
  jest.resetModules();
  // manually reset mocks
  jest.dontMock('@okta/jwt-verifier');
  jest.dontMock('axios');
});

//
//
test('1) oidc token not active anymore but refresh succeded', async () => {
  const mockedVerifyAccessToken = jest.fn().mockResolvedValue();
  jest.doMock(
    '@okta/jwt-verifier',
    () =>
      function OktaJwtVerifier() {
        return {
          verifyAccessToken: mockedVerifyAccessToken,
        };
      }
  );

  // ...
});


//
//
test('2) mock axios', async () => {
  jest.doMock(
    'axios',
    // ...
  );

  // ...
});

//
//
test('3) mocks should be reset from beforeEach', async () => {
  // ...
});

So you have to keep a list of all modules that you want to mock in one or multiple tests but the order of the tests doesn’t matter anymore (or .only/.skip or running all tests).

afterEach(() => {
    jest.resetModules();
  });

try it to solve direct mock implementations

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

It worked fine to me.

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.