Quick & Easy Way To Reset Mocks & Spies In Jest

When working with mocks and spies in Jest, it is quite easy to fall into a trap where they become stale (especially in cases where pure functions are not being used). Heading to the documentation for Jest yields a lot of similar-looking methods for restoring mocks, clearing mocks and resetting mocks.

This is where confusion sets in. What is the best practice? Which ones should I call to ensure my tests don’t have stale mocks or spies? Even I struggled with this aspect.

In my Aurelia applications, Jest is my prefered means of test tool. In my trial and error, I have settled on the following in my tests which ensures all mocks and spies are reset between tests being run.

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

The jest.resetAllMocks method resets the state of all mocks in use in your tests. It is the equivalent of manually calling mockReset on every mock you have (which can be tedious if you have a lot of them).

The jest.restoreAllMocks method restores all mocks back to their original value, ONLY if you used jest.spyOn to spy on methods and values in your application. It is important that you use spyOn where you possibly can.

How To Easily Mock Moment.js In Jest

Recently whilst writing some unit tests in Jest, I had to test some code that took ISO date strings and converted them to formatted date strings, then code that converts them back to ISO strings before it’s sent to the server.

My first attempt was to use jest.mock and mock each individual method. For some of the uses of moment where simple dates are being converted, it is easy enough to mock format and other methods, but once you start chaining Moment methods, things get tricky from a mocking perspective.

This is some code that would be a nightmare to mock in Jest:

moment.utc().add('1', 'years').format('YYYY')

It turns out there is a much easier way to “mock” moment, without actually mocking it at all. You get a fully functional (well in my use case) version of Moment that actually converts dates and allows you to use chaining features.

jest.mock('moment', () => {
  const moment = jest.requireActual('moment');

  return {default: moment };
});

You use the jest.requireActual method to require the real Moment package, then you return it inside of an object. I am having to return it with default because moment is being included in my application like this:

import moment from 'moment';

It’s a surprisingly simple, functional and elegant solution. It requires no absurd nested mock functions and code. If for whatever reason you need to override certain Moment methods, you can do so either inside of the mock declaration or on a per-use basis.

Jest Not Finding Tests In Travis CI? You Might Be Ignoring The Build Folder

Recently, I encountered an issue in Travis CI and my Aurelia application which uses Jest for the tests suite. I erroneously copied some older configuration code for Jest and used it in my Jest configuration, assuming it would work.

This error took a while to isolate and resolve. It was out of pure desperate that I accidentally discovered the fix. If I made the mistake, it is possible that you might as well. Does this sound familiar to you?

Tests were working locally. Running Jest would run my tests and they would all pass, giving a nice 0 code, happy days. Pushing my changes up causing my Travis CI build to automatically run would result in an error. The error looked like the following (this is the actual error):

One thing you will notice is my testRegex is saying there are 3 matches, which is correct. I have three test files which run and work locally without issue. I tried modifying my testRegex I tried changing the location of my tests, nothing worked. Then I noticed I had testPathIgnorePatterns defined in my Jest configuration (part of my older config).

This is my actual code. I was ignoring my build, dist and sample directories from testing. It turns out defining this locally causes no problem, but causes Travis CI to fail. At first, I was confused, why is this breaking Travis CI.

Then it dawned on me. It’s the fact I am using some outdated configuration option, the issue is the path in which tests and code are run inside of the virtualised Travis CI environment.

Can you spot the issue? The path is /home/travis/build/steem-engine-exchange/steem-engine-dex what is contained within this path? A folder named buildwhich is located under /home/travis ignoring build causes this to get matched and thus, it breaks the tests from running.

My innocent ignore rule was being triggered only in Travis CI, not locally because locally my files are run from my home directory and not within a build directory.