jest.clearAllMocks vs jest.resetAllMocks vs jest.restoreAllMocks explained
Tags: jest, javascript, testing, beginners
Published: 2022-08-31 16:08:57

I know is been hard to remember and confuse about what these clearAllMocks, resetAllMocks and restoreAllMocks actually does and why we need that? 

Allow me to clear your mind!

## jest.clearAllMocks
Clear all mock usage data such as `mock.calls`, `mock.instances`, `mock.contexts` and `mock.results` but not their implementation.

Every time the mock function get called it will keep those usage contexts into the mock function object itself. 

Let's take a following example. We going to test the `getRandomNumber` that actually calling `randomNumberGenerator()` to get the random number. So, In this case we will mock the `random.service` module.

```javascript
// main.test.js
jest.mock('random.service')

const { randomNumberGenerator } = require('random.service')

const getRandomNumber => randomNumberGenerator()

it('should return a number', () => {
  randomNumberGenerator.mockReturnValue(7)
  const num = getRandomNumber()

  console.log(randomNumberGenerator.mock) // 👈 check this out!
  
  expect(num).toBe(7)
})
```
Once you call `getRandomNumber` function, the mock `randomNumberGenerator` will also get called accordingly. After this, you can `console.log(randomNumberGenerator.mock)` and you will see something like this.
```javascript
{
  calls: [ [] ],
  contexts: [
    <ref *1> {...}
  ],
  instances: [
    <ref *1> {...}
  ],
  invocationCallOrder: [ 1 ],
  results: [ { type: 'return', value: 2 } ],
  lastCall: []
}
```
The contexts will be useful for some assertions purpose like to expect number of calls, do function have return and more. This also could be useful for certain use case that may need these context for cross test case assertions. 

However, this also could lead to some problem if you don't clear it. Example below
```javascript
it('example 1', () => {
  randomNumberGenerator.mockReturnValue(7)
  const num = getRandomNumber()
  expect(randomNumberGenerator).toBeCalledTimes(1)
})

it('example 2', () => {
  randomNumberGenerator.mockReturnValue(8)
  const num = getRandomNumber()
  expect(randomNumberGenerator).toBeCalledTimes(1) // This will failed!! because it expect 2 times.
})
```
The second example will failed and this is because the `mock.calls` has 2 counts. To fix it, you need to use `jest.clearAllMocks` in jest global like `afterEach` or `beforeEach`
```javascript
beforeEach(() => {
  jest.clearAllMocks()
})
```
This tell jest to clear all the mock usage data before the next test case start.

## jest.resetAllMocks
A superset of `clearAllMocks()` and it also reset the mock function implementations with brand new `jest.fn()`.

By default, all mock function without implementation it will always return `undefined`. And once added implementation it will keep the implementation in the mock function object.

Following code will explain everything.
```javascript
it('example 1', () => {
  randomNumberGenerator.mockReturnValue(7)
  const num = getRandomNumber()
  expect(num).toBe(7)
})

it('example 2', () => {

  // we didn't mock `randomNumberGenerator` in this test but this will return 7 because the last test case is added implementation.
  console.log(randomNumberGenerator())

  const num = getRandomNumber()
  expect(num).toBe(7)
})
```

This is useful when you need re-usable the mock function. So you don't have to write again in every test case. 

If you want every test case is fresh, new and standalone. You should use it before each test case start. It can ensure side effect free for each test case and worry less.

## jest.restoreAllMocks
Restore all mock back to their original implementation and it only works for mock was created with `jest.spyOn`. 

For some reason, you need to mock the module partially with certain function. A classic example will be testing the date time related as following example.

```javascript
it('should be my birthday', () => {
  jest.spyOn(Date, 'now').mockReturnValue('2022-08-31T15:23:19.576Z')
  const result = isTodayMyBirthday()
  expect(result).toBe(true)
})
```

This works well but it is dangerous because spyOn is kind of like mutate the module object and if you forgot to restore all mocks, the subsequent test is using the mock version of `Date.now()` and this is not really obvious and hard to debug.

Like previous solution did, just clear it before every next test case.
```javascript
beforeEach(() => {
  jest.restoreAllMocks()
})
```

Be cautions when you see spyOn in your test case it could be the reason to make your test failed.

## Conclusion
As you can see, the mock API behavior is always keep the state and you have to clear/reset/restore it explicitly. There is a better to do this if you want your test case always start with a new fresh and side effect free. You can do it in jest config.

```javascript
// jest.config.js

const config = {
  clearMocks: true,
  resetMocks: true,
  restoreMocks: true
}
```

I hope this will help you have better understanding. Like or Unicorn or Save if it does help! Thank you.