ResetAllMocks、 resetModule、 resetModuleRegistry、 restoreAllMocks 在 Jest 中的差异

我正在努力理解下面的笑话:

resetAllMocksresetModulesresetModuleRegistryrestoreAllMocks

我发现这很困难。

我看了一些搞笑的文件,但是不太清楚。如果有人能为我提供一个例子,以上工作,他们是如何彼此不同,我将不胜感激。

51633 次浏览

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:

  1. If you want to test mock function called times, clear before you use
  2. If you want to make sure mock return value wouldn't pollute other test case, call reset
  3. If you want to use origin method instead of mock implementation, call 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);
});
});