Unit tests...clean after yourself and sometimes after others too
Tags: javascript, testing, jest, typescript
Published: 2023-09-26 10:25:30

We've all experienced working on projects with multiple contributors, or even just a few. In such projects, unit tests are typically written by various individuals and sometimes have dependencies. For instance, it's common to encounter scenarios where consecutive tests mock the same functions. If these tests aren't written correctly, they can often lead to subsequent test failures due to inadequate mock cleanup.

Having encountered several instances of this issue, I've chosen to share a few tips for improving unit tests in Jest. These principles can also be applied to other testing frameworks like Mocha, Ava etc.

## Clean up after others

When examining a single unit test file, it's impossible to predict which tests will be added in the future, potentially in separate modules and files. Therefore, it is highly recommended to ensure that our testing environment is as clean as possible before starting to write additional unit-tests.

This goal can be accomplished by utlizing the use of `jest.resetAllMocks` and `jest.restoreAllMocks` within our `beforeAll` block.

The difference between `jest.restoreAllMocks` and `jest.resetAllMocks` lies in the fact that `jest.resetAllMocks` internally invokes `clearAllMocks()` (which erases all mock usage data, such as `mock.calls`, `mock.instances`, `mock.results` and `mock.contexts`), it also reverts the mock function implementations to brand new instances created with `jest.fn()`.

In contrast, `jest.restoreAllMocks` reverts all mocks to their original implementations, a functionality that is specific to mocks created using `jest.spyOn`.


### An example on the utilization of `jest.restoreAllMocks` and `jest.resetAllMocks`

_I also prefer use `beforeAll` to set my spies and mocks after cleaning the environment, and `beforeEach` to set the relevant mock responses._

```
let getDataSpy: jest.SpyInstance;
let getUserContentbyIdMock: jest.Mock;

beforeAll(() => {
  
  jest.resetAllMocks();
  jest.restoreAllMocks();

  getDataSpy = jest.spyOn(userDataSdk, "getData");

  getUserContentbyIdMock = jest.fn();
});

beforeEach(() => {
  getDataSpy.mockResolvedValue(getDataResponse);
  getUserContentbyIdMock.mockResolvedValue(contentMockResponse);
});
```

## Clean up after yourself

Just as we tidy up after others, it's important to ensure we clean up after each `it` block in our test file and also reset our environment before exiting our unit test file.

In order to do that, we need to use `jest.restoreAllMocks` and `jest.resetAllMocks`

### An example on the utilization of `jest.restoreAllMocks` and `jest.resetAllMocks` after our tests complete

_(It's enough to put `jest.resetAllMocks()` in `afterEach` because it will always run after the last `it` finishes in any case)_

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

  afterAll(() => {
    jest.restoreAllMocks();
  });
```


## Conclusion

Making sure our unit-test environment and setup is clean from previous data and mocks is a must, it ensures that our tests run in the isolated manner they are supposed to.
And as you can see, achieving this is quite simple and can save you a lot of frustration and debugging time in the future.

Also would like to recommend this [post](https://dev.to/edwinwong90/jestclearallmocks-vs-jestresetallmocks-vs-jestrestoreallmocks-explained-5aal) by [Edwin Wong](https://dev.to/edwinwong90) to learn more about the differences between `jest.resetAllMocks`, `jest.restoreAllMocks` and `jest.clearAllMocks`.