jest
jest copied to clipboard
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
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?