JulienKarst.

Navigate back to the homepage

How to mock a javascript module with jest

Julien Karst
December 15th, 2019 · 2 min read

How to mock a javascript module with jest

In this post we will see how to mock a javascript module using jest. This post assume that you already have knowledge about Javascript and testing.

How modules caching works with node.js ?

The node.js documentation says: ”Modules are cached after the first time they are loaded”. This means that every other call that require the same file will get exactly the same object.

Let’s see how it works with a concrete example. First we will create a file target.js

1module.exports = {
2 example: () => console.log("I'm the original module"),
3};

Inside we have nothing crazy. It’s just a simple module.exports of an object that contain the key example and the value is a function that calls console.log. Now let’s create another file example.js

1const targetPath = require.resolve('./target.js');
2
3console.log(require.cache[targetPath]);
4const target = require('./target');
5console.log(require.cache[targetPath]);
6
7target.example();

Now we will execute our example file with node: node ./example.js and we can see:

  • The first log show undefined it means that the module is not loaded into the yet because it was not required for the moment
  • The second log show the cached module !
    1{
    2 id: 'FULL_PATH/target.js',
    3 exports: { example: [Function: example] },
    4 loaded: true,
    5 // Other elements ...
    6 }
  • Then we see that we called the original module
    1I'm the original module

In this module we can see:

  • id which is the path of the file
  • exports which is the exported content of the file
  • loaded which is the loaded state of the module

So when a module is loaded it goes to cache, why not just putting our mock in the cache to override it ? Let’s try that open example.js again. We will add the loaded value to true set name and set the id to the target path. Then the most important part is in export, this is where we will add our mock.

1const targetPath = require.resolve('./target.js');
2require.cache[targetPath] = {
3 loaded: true,
4 id: targetPath,
5 exports: {
6 example: () => console.log("I'm mocked"),
7 },
8};
9
10const target = require('./target');
11console.log(require.cache[targetPath]);
12
13target.example();

We can run that file again: node ./example.js. We can see ""I'm mocked" which mean the original module is not required anymore. Our mock is called !

How to restore to use the original module ? It’s actually pretty simple to invalidate the cache, you can do delete require.cache[targetPath] and on the next require it will not use your mocked module.

So now you know how node modules caching works and how testing tools will mock them. It think this is important to know what’s happening under the hoods so you can use the tool more efficiently. Let’s now see how we can do that using jest.

How to mock an object with jest ?

To mock a javascript module using jest we can use the mock method. This method takes two arguments:

  • the path of the module
  • a factory that create the mock object
1const target = require('./target');
2jest.mock('./target', () => ({
3 example: jest.fn(() => console.log("I'm mocked")),
4}));

Doing that jest will do what we’ve made in the previous section by overriding the module cache.

ES Modules mocking

But how this can works with ES modules imports as they must be on top of the file ? Actually calls to jest.mock are hoisted on the top of the file. This is why we have that magic. The jest documentation talk about that in a dedicated section:

If you’re using ES module imports then you’ll normally be inclined to put your import statements at the top of the test file. But often you need to instruct Jest to use a mock before modules use it. For this reason, Jest will automatically hoist jest.mock calls to the top of the module (before any imports).

More articles from Julien Karst

How to setup circleci inside a monorepo

You might want to setup a ci for your monorepo. In this article we will see how to use Circleci inside a mono-repository. This post assume that you already have knowledge about monorepo and continuous integration process with Circleci.

November 19th, 2019 · 2 min read
© 2019 Julien Karst
Link to $https://twitter.com/julienkarstLink to $https://github.com/JulienKodeLink to $https://instagram.com/julien.karstLink to $https://www.linkedin.com/in/julienkarst