I'm trying to wrap my head around the following in Jest:
resetAllMocks
, resetModules
, resetModuleRegistry
and restoreAllMocks
and I'm finding it difficult.
I read the jest documentation but it's not too clear. I would appreciate it if someone can please provide me with an example of how the above work and they are different from each other.
clearAllMocks() and clearMocks:[boolean] Resets all the mocks usage data, not their implementation. In other words, it only replaces fn. mock.
You can create a namespace that you export as the default object and call b using the namespace. This way, when you call jest. mock it will replace the b function on the namespace object. const f = require('./f'); jest.
Mock functions allow you to test the links between code by erasing the actual implementation of a function, capturing calls to the function (and the parameters passed in those calls), capturing instances of constructor functions when instantiated with new , and allowing test-time configuration of return values.
To spy on an exported function in jest, you need to import all named exports and provide that object to the jest. spyOn function. That would look like this: import * as moduleApi from '@module/api'; // Somewhere in your test case or test suite jest.
It resets Jest's module registry which is a cache for all required/imported modules. Jest will re-import any required module after a call to this. Imagine a clean slate without having to deal with all the mocked out modules in other tests.
Since “restoreMocks”: true automatically restores a spy prior to executing each unit test spec (and prior to any custom beforeEach (..) ), it's best to only use jest.spyOn ( ..) inside either: a beforeEach ( ..) Whereas the following usage of jest.spyOn ( ..) will give issues:
Modules that are mocked with jest.mock are mocked only for the file that calls jest.mock. Another file that imports the module will get the original implementation even if it runs after the test file that mocks the module. Returns the jest object for chaining.
Resets all the mocks usage data, not their implementation. In other words, it only replaces fn.mock.calls and fn.mock.instances properties of a jest mock function. A superset of clearAllMocks () which also takes care of resetting the implementation to a no return function.
The following sections explain the behaviors of each function and its corresponding config directive. In the case of config directives, the explained behavior takes place in between each test making them more and more isolated from the other tests.
References to fn
are implying a sample jest mock function under each of these actions.
jest.clearAllMocks()
and clearMocks:[boolean]
Resets all the mocks usage data, not their implementation. In other words, it only replaces fn.mock.calls
and fn.mock.instances
properties of a jest mock function.
jest.resetAllMocks()
and the resetMocks:[boolean]
A superset of clearAllMocks()
which also takes care of resetting the implementation to a no return
function. In other words, it will replace the mock function with a new jest.fn()
, not just its fn.mock.calls
and fn.mock.instances
.
jest.restoreAllMocks()
and restoreMocks:[boolean]
Similar to resetAllMocks()
, with one very important difference. It restores the original implementation of "spies". So, it goes like "replace mocks with jest.fn(), but replace spies with their original implementation".
So, in cases where we manually assign things with jest.fn() (not spies), we have to take care of implementation restoration ourselves as jest won't be doing it.
jest.resetModules()
and resetModules:[boolean]
It resets Jest's module registry which is a cache for all required/imported modules. Jest will re-import any required module after a call to this. Imagine a clean slate without having to deal with all the mocked out modules in other tests.
jest.resetModuleRegistry
It's just an alias for resetModules
, see:
https://github.com/facebook/jest/blob/7f69176c/packages/jest-runtime/src/index.ts#L1147
See how clearing, resetting and restoring differ in action:
https://repl.it/@sepehr/jest-mock-api-reset-restore#jest-mock-apis.test.js
PASS ./jest-mock-apis.test.js
jest mock reset/restore api
when calling mockReset() on a test double with custom impl.
if the test double is a spy
✓ jest replaces the impl. to a new undefined-returning jest.fn() (18ms)
if the test double is "not" a spy
✓ jest replaces the impl. to a new undefined-returning jest.fn() (17ms)
when calling mockRestore() on a test double with custom impl.
if the test double is "not" a spy
✓ jest resets the impl. to a new undefined-returning jest.fn() (2ms)
if the test double is a spy
✓ jest restores the original impl. of that spy (7ms)
describe('jest mock reset/restore api', () => {
describe('when calling mockReset() on a test double with custom impl.', () => {
describe('if the test double is a spy', () => {
test('jest replaces the impl. to a new undefined-returning jest.fn()', () => {
const module = { api: () => 'actual' }
jest.spyOn(module, 'api').mockImplementation(() => 'spy mocked')
expect(module.api()).toStrictEqual('spy mocked')
expect(module.api).toHaveBeenCalledTimes(1)
module.api.mockReset()
expect(module.api()).toStrictEqual(undefined)
expect(module.api).toHaveBeenCalledTimes(1)
})
})
describe('if the test double is "not" a spy', () => {
test('jest replaces the impl. to a new undefined-returning jest.fn()', () => {
const api = jest.fn(() => 'non-spy mocked')
expect(api()).toStrictEqual('non-spy mocked')
expect(api).toHaveBeenCalledTimes(1)
api.mockReset()
expect(api()).toStrictEqual(undefined)
expect(api).toHaveBeenCalledTimes(1)
})
})
})
describe('when calling mockRestore() on a test double with custom impl.', () => {
describe('if the test double is "not" a spy', () => {
test('jest resets the impl. to a new undefined-returning jest.fn()', () => {
const api = jest.fn(() => 'non-spy mocked')
expect(api()).toStrictEqual('non-spy mocked')
expect(api).toHaveBeenCalledTimes(1)
api.mockRestore()
expect(api()).toStrictEqual(undefined)
expect(api).toHaveBeenCalledTimes(1)
})
})
describe('if the test double is a spy', () => {
test('jest restores the original impl. of that spy', () => {
const module = { api: () => 'actual' }
jest.spyOn(module, 'api').mockImplementation(() => 'spy mocked')
expect(module.api()).toStrictEqual('spy mocked')
expect(module.api).toHaveBeenCalledTimes(1)
module.api.mockRestore()
expect(module.api()).toStrictEqual('actual')
expect(module.api).not.toHaveProperty('mock')
})
})
})
})
Thanks for @sepehr answer.
I think it would be easier to understand by example.
Quick tips:
clear
before you usereset
restore
.import {Calculator} from './calculator';
describe('calculator add', function () {
let calculator = new Calculator();
const mockAdd = jest.spyOn(calculator, 'add');
it('mock the add method', function () {
calculator.add = mockAdd.mockReturnValue(5);
expect(calculator.add(1, 2)).toBe(5);
});
it('because we didnt clear mock, the call times is 2', function () {
expect(calculator.add(1, 2)).toBe(5);
expect(mockAdd).toHaveBeenCalledTimes(2);
});
it('After clear, now call times should be 1', function () {
jest.clearAllMocks();
expect(calculator.add(1, 2)).toBe(5);
expect(mockAdd).toHaveBeenCalledTimes(1);
});
it('we reset mock, it means the mock has no return. The value would be undefined', function () {
jest.resetAllMocks();
expect(calculator.add(1, 2)).toBe(undefined);
});
it('we restore the mock to original method, so it should work as normal add.', function () {
jest.restoreAllMocks();
expect(calculator.add(1, 2)).toBe(3);
});
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With