183

I have a spy that is used in multiple assertions across multiple tests in a suite.

How do I clear or reset the spy so that in each test the method that the spy intercepts is considered not to have been invoked?

For example, how to make the assertion in 'does not run method' be true?

const methods = {
  run: () => {}
}

const spy = jest.spyOn(methods, 'run')

describe('spy', () => {
  it('runs method', () => {
    methods.run()
    expect(spy).toHaveBeenCalled() //=> true
  })

  it('does not run method', () => {
    // how to make this true?
    expect(spy).not.toHaveBeenCalled() //=> false
  })
})
sdgluck
  • 24,894
  • 8
  • 75
  • 90

5 Answers5

246

Thanks to @sdgluck for the answer, though I would like to add to this answer that in my case, I wanted a clear state after each tests since I have multiple tests with same spy. So instead of calling the mockClear() in previous test(s), I moved it into the afterEach() (or you can use it with beforeEach) like so:

afterEach(() => {    
  jest.clearAllMocks();
});

And finally, my tests are working like they should without the spy being called from previous test. You can also read their documentation for it

Option 2

If you wish to do it from a global level, you could also updated your jest.config.js (or from package.json)

module.exports = {
  clearMocks: true,
  // ...
}

You can read Jest documentation about it

ghiscoding
  • 12,308
  • 6
  • 69
  • 112
  • 70
    There is also `jest.restoreAllMocks();` if you want to restore mocked functions back to their original implementations! – ZorleQ Apr 07 '20 at 18:03
  • 14
    You can also add `restoreMocks: true` to your Jest config to automatically have it call `restoreAllMocks()` after each test. Personally, I have zero reason to ever let mocks persist between any two tests, so I like to blindly reset them all between each test without having to write it out in the `afterEach()` block as a cleanup item. – user2490003 Dec 22 '20 at 06:08
  • 1
    You can call `jest.clearAllMocks();` in-between two assertions in the same test suite as well. This is useful for cases in which you decide to group all tests for a single method under one `test suite, but you want the assertions to not remember their previous values. – Nadav Aug 04 '21 at 00:57
  • 1
    You can simply use `afterEach(jest.clearAllMocks)` – trevorgk Aug 30 '21 at 19:53
75

Jest spies have the same API as mocks. The documentation for mocks is here and specifies a method mockClear which:

Resets all information stored in the mockFn.mock.calls and mockFn.mock.instances arrays.

Often this is useful when you want to clean up a mock's usage data between two assertions.

(emphasis my own)

So we can use mockClear to "reset" a spy. Using your example:

const methods = {
  run: () => {}
}

const spy = jest.spyOn(methods, 'run')

describe('spy', () => {
  it('runs method', () => {
    methods.run()
    expect(spy).toHaveBeenCalled() //=> true
    /* clean up the spy so future assertions
       are unaffected by invocations of the method
       in this test */
    spy.mockClear()
  })

  it('does not run method', () => {
    expect(spy).not.toHaveBeenCalled() //=> true
  })
})

Here is an example in CodeSandbox.

sdgluck
  • 24,894
  • 8
  • 75
  • 90
  • 5
    Might want to update this. As of today, using Jasmine ~2.8.6 and jest 24.0.9, this is incorrect. `Property 'mockClear' does not exist on type 'Spy'.` – Bardicer Jan 22 '20 at 18:42
  • @Bardicer This approach still works in the latest Jest, and is correct according to the Jest documentation. Please see the link I have put at the end of my answer which demonstrates it working in CodeSandbox. – sdgluck May 20 '21 at 12:34
  • 2
    ...well then... I guess I just tried to make it work on a day jest was taking off. Wouldn't be the first time a library has just decided to be difficult and not work as expected. – Bardicer May 21 '21 at 14:43
  • Hrm... I'm seeing what @Bardicer saw. Version '2.99.0'. You sure you don't have [jest-mock-extended](https://github.com/marchaos/jest-mock-extended) or similar installed? "_jest-mock-extended exposes a mockClear and mockReset for resetting or clearing mocks..._" – ruffin Jan 20 '23 at 20:40
27

In case you want to restore the original behavior of a method you had previously added to a spy, you can use the mockRestore method.

Check out the example bellow:

class MyClass {
    get myBooleanMethod(): boolean {
        return true;
    }
}

const myObject = new MyClass();
const mockMyBooleanMethod = jest.spyOn(myObject, 'myBooleanMethod', 'get');
// mock myBooleanMethod to return false
mockMyBooleanMethod.mockReturnValue(false);
// restore myBooleanMethod to its original behavior
mockMyBooleanMethod.mockRestore();
Daniel Dantas
  • 371
  • 3
  • 5
  • thanks @Daniel, this one was the only one that worked for me in my situation wherein the broken test was doing spy = jest.spyOn(document) which needed to be done twice and the 2nd time will cause "TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'." – Carlos Jaime C. De Leon Mar 19 '23 at 23:56
  • Same here, after wasting couple of hours, this is the only solution that works – Aishwat Singh Jun 13 '23 at 15:37
16

Iterating further on @ghiscoding's answer, you can specify clearMocks in the Jest config, which is equivalent to calling jest.clearAllMocks() between each test:

{
...
    clearMocks: true,
...
}

See the docs here.

James Irwin
  • 1,171
  • 8
  • 21
1

In jest.config.js add mockReset - not clearMocks, as that will not remove any mock implementation between tests (i.e. mockReset is the more cautious option and will remove implementation between tests - which in my opinion is the right thing to do given each tests is supposed to be isolated).

{
...
    mockReset: true,
...
}
Jeremy
  • 1,170
  • 9
  • 26