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.