Jest: mockClear vs mockReset vs mockRestore

Created on 20 Dec 2017  路  9Comments  路  Source: facebook/jest

Do you want to request a _feature_ or report a _bug_?

I would like to ask a question about a feature and then submit a PR to the docs based on the answer.

What is the current behavior?

The docs for mockReset say this:

Resets all information stored in the mock, including any initial implementation and mock name given.

This is useful when you want to completely restore a mock back to its initial state.

What is the expected behavior?

I'm not sure what mockRestore does exactly in the context of restoring. Do I need it for restoring a mock's initial implementation? For example:

it('my test', () => {
  const prop = jest.spyOn(obj, 'prop').mockImplementation(() => { /* ... */ });
  // do something with obj.prop
  prop.mockRestore();
});

Is the above enough? Do I need to also call mockReset or maybe even mockClear beforehand? Why do the docs say that mockReset is useful for restoring mock to its initial state?

My main question: what is mockReset useful for?

Question

Most helpful comment

Hey all, think this issue is worth looking into again. The clear, reset, restore distinction isn't the most intuitive.

The site's pretty good on distinguishing the difference between mockReset() and mockClear(). mockReset() is a supersized version of mockClear(). mockClear() resets usage data but not implementation. mockRestore() resets everything, which includes usage data, implementation and mock name.

I also like that restoreMocks() config option points users to the jest.restoreAllMocks() method, which then points to the mockRestore() method. And so forth for their reset and clear counterparts.

Where I see the most confusing is the difference between mockRestore() and mockReset()

mockFn.mockReset()

Resets all information stored in the mock, including any initial implementation and mock name given.

This is useful when you want to completely restore a mock back to its initial state.

Beware that mockReset will replace mockFn.mock, not just mockFn.mock.calls and mockFn.mock.instances. You should therefore avoid assigning mockFn.mock to other variables, temporary or not, to make sure you don't access stale data.

mockFn.mockRestore()

Removes the mock and restores the initial 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 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.

There's two main points of confusion here for me. The first is that both mention resetting implementation. The second is that mockRestore() mentions only working on spies.

Questions that could be clarified:

  • Does mockReset() reset implementation for spies?
  • Does mockRestore() do anything for non-spy mocks?
  • What does it mean to "take care of restoration yourself when manually assigning jest.fn()"?

All 9 comments

In your snippet mockRestore restores obj.prop back to its initial state. Instead of

const origProp = obj.prop;
obj.prop = jest.fn().mockImplementation(() => { /* ... */ });
// do something with obj.prop
obj.prop = origProp;

You do not have to call reset or clear.

Did that help? PR very much welcome for clarification to the docs 馃檪

Thanks, that did help. 馃憤 I'm just wondering when is mockReset useful, i.e. when you would reset a mock instead of simply creating a new one with jest.fn(). I'll try to think of some examples and submit a PR to the docs.

For instance:

const mockAProp = jest.fn();

jest.mock('some-dep', () => ({ foobar: mockAProp }));

afterEach(() => {
    mockAProp.mockReset();
});

test('test 1', () => {
    mockAProp.mockImplementation(() => true);
});

test('test 2', () => {
    mockAProp.mockImplementation(() => true);
});

Thanks!

Hey all, think this issue is worth looking into again. The clear, reset, restore distinction isn't the most intuitive.

The site's pretty good on distinguishing the difference between mockReset() and mockClear(). mockReset() is a supersized version of mockClear(). mockClear() resets usage data but not implementation. mockRestore() resets everything, which includes usage data, implementation and mock name.

I also like that restoreMocks() config option points users to the jest.restoreAllMocks() method, which then points to the mockRestore() method. And so forth for their reset and clear counterparts.

Where I see the most confusing is the difference between mockRestore() and mockReset()

mockFn.mockReset()

Resets all information stored in the mock, including any initial implementation and mock name given.

This is useful when you want to completely restore a mock back to its initial state.

Beware that mockReset will replace mockFn.mock, not just mockFn.mock.calls and mockFn.mock.instances. You should therefore avoid assigning mockFn.mock to other variables, temporary or not, to make sure you don't access stale data.

mockFn.mockRestore()

Removes the mock and restores the initial 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 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.

There's two main points of confusion here for me. The first is that both mention resetting implementation. The second is that mockRestore() mentions only working on spies.

Questions that could be clarified:

  • Does mockReset() reset implementation for spies?
  • Does mockRestore() do anything for non-spy mocks?
  • What does it mean to "take care of restoration yourself when manually assigning jest.fn()"?

Any updates on this?

The updated docs doesn't help? #6227

Currently I'm just adding the following any time I use mocks since I believe this should cover all cases whether I use spies or mocks.

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

FTR, here are the answers to the questions raised by @geoffreyyip.

_"Does mockReset() reset implementation for spies?"_

No. It resets the implementation to a new undefined-returning mock function.

_"Does mockRestore() do anything for non-spy mocks?"_

Yes. It resets the implementation to a new undefined-returning mock function. For spies, it "restores" the original implementation.

_"What does it mean to "take care of restoration yourself when manually assigning jest.fn()"?"_

Here's an example:

// contrary to jest.spyOn(), jest.fn() has "replaces" the original implementation
// without keeping a copy for later restoration. 
test('...', () => {
  const origFn = module.api
  module.api = jest.fn() 

  // act, assert, etc.

  // manual restoration
  module.api = origFn
})

Here's a passing test suite that clarifies how mockReset() and mockRestore() behavior differs.

You may run it online:
https://repl.it/@sepehr/jest-mock-api-reset-restore

describe('jest mock reset/restore api', () => {

  describe('when calling mockReset()', () => {
    test('on a spy with custom implementation, it replaces the implementation to a nenw undefined-returning mock 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)
    })

    test('on a non-spy with custom implementation, it replaces the implementation to a new undefined-returning mock 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()', () => {
    test('on a non-spy mock with custom implementation, it resets the implementation to a new undefined-returning mock 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)
    })

    test('on a spy with custom implementation, it resets the implementation to a new undefined-returning mock 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.mockRestore()

      expect(module.api()).toStrictEqual('actual')
      expect(module.api).not.toHaveProperty('mock')
    })
  })

})
Was this page helpful?
0 / 5 - 0 ratings