w3resource

Manual Mocks in Jest


We use manual mocks to stub out functionality with mock data. For instance, rather than accessing a remote resource like a website or a database, you may want to create a manual mock that will allow you to use fake data. This will ensure your tests will be fast and not flaky.

Mocking user modules

We can define Manual mocks by writing a module in a __mocks__/ subdirectory immediately adjacent to the module. For instance, if you want to mock a module called user in the models directory, you need to create a file called user.js and put it in the models/__mocks__ directory. Note however, that the __mocks__ folder is case-sensitive, thus, naming the directory __MOCKS__ will break on some systems.

Whenever we require that module in our tests, we are required to explicitly call jest.mock('./moduleName').

Mocking Node modules

In the case where the module you are mocking is a Node module (e.g.: lodash), the mock needs to be placed in the __mocks__ directory adjacent to node_modules (unless you have configured roots to point to a folder other than the project root) and is automatically mocked. Explicitly calling jest.mock('module_name') is not needed.

You can mock Scoped by creating a file in a directory structure that matches the name of the scoped module. For instance, if you want to mock a scoped module called @scope/project-name, you need to create a file at __mocks__/@scope/project-name.js, and creating the @scope/ directory accordingly.

Warning: In the case where you want to mock Node's core modules (e.g.: fs or path), then you should explicit call e.g. jest.mock('path') is required, because core Node modules will not mocked by default.

Examples


+-- config
+-- __mocks__
?   +-- fs.js
+-- models
?   +-- __mocks__
?   ?   +-- user.js
?   +-- user.js
+-- node_modules
+-- views

When a given module has a manual mock, Jest's module system uses that module when explicitly calling jest.mock('moduleName'). However, when you set automock to true, the manual mock implementation is used rather than the automatically created mock, even if you don?t call jest.mock('moduleName'). If you want to opt out of this behavior you will need to explicitly call jest.unmock('moduleName') in tests that should use the actual module implementation.

Note: To mock properly, Jest will need jest.mock('moduleName') to be in the same scope as the require/import statement.

Here's a contrived example of a module that provides a summary of all the files in a given directory. In this case we will use the core (built in) fs module.

 FileSummarizer.js
```'use strict';

const fs = require('fs');

function summarizeFilesInDirectorySync(directory) {
  return fs.readdirSync(directory).map(fileName => ({
    directory,
    fileName,
  }));
}
exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;

Since we would prefer our tests to avoid actually hitting the disk (that's pretty slow and fragile), we need to create a manual mock for the fs module by extending an automatic mock. Our manual mock implements custom versions of the fs APIs that we can build on for our tests:

// __mocks__/fs.js
```'use strict';

const path = require('path');

const fs = jest.genMockFromModule('fs');

// This is a custom function that can be used by our tests during setup to specify
// what the files on the "mock" filesystem needs to look like when any of the
// `fs` APIs are used.
let mockFiles = Object.create(null);
function __setMockFiles(newMockFiles) {
  mockFiles = Object.create(null);
  for (const file in newMockFiles) {
    const dir = path.dirname(file);

    if (!mockFiles[dir]) {
      mockFiles[dir] = [];
    }
    mockFiles[dir].push(path.basename(file));
  }
}

// This is a custom version of `readdirSync` that reads from the special mocked out
// file list that is set via __setMockFiles
function readdirSync(directoryPath) {
  return mockFiles[directoryPath] || [];
}

fs.__setMockFiles = __setMockFiles;
fs.readdirSync = readdirSync;

module.exports = fs;

Now to write our test. You should note that we need to explicitly tell that we want to mock the fs module because it is a core Node module:

// __tests__/FileSummarizer-test.js
```'use strict';

jest.mock('fs');

describe('listFilesInDirectorySync', () => {
  const MOCK_FILE_INFO = {
    '/path/to/file1.js': 'console.log("file1 contents");',
    '/path/to/file2.txt': 'file2 contents',
  };

  beforeEach(() => {
    // Set up some mocked out file info before every test
    require('fs').__setMockFiles(MOCK_FILE_INFO);
  });

  test('includes all files in the directory in the summary', () => {
    const FileSummarizer = require('../FileSummarizer');
    const fileSummary = FileSummarizer.summarizeFilesInDirectorySync(
      '/path/to',
    );

    expect(fileSummary.length).toBe(2);
  });
});

The example mock above uses jest.genMockFromModule to generate an automatic mock, and will override its default behavior. This approach is recommended, but it is completely optional. If you do not want to make use of the automatic mock at all, you can export your own functions from the mock file. One downside to fully manual mocks is that they are manual ? this means that you have to manually update them any time the module they are mocking changes. Hence, it is usually best to use or extend the automatic mock when it works for your tests.

In the case where you want to ensure that a manual mock and its real implementation are in sync, it might be useful to require the real module by using jest.requireActual(moduleName) in your manual mock and then amending it with mock functions before exporting it.

Using with ES module imports

In the case where 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 have to instruct Jest to use a mock before modules use it. For this reason, Jest automatically hoists jest.mock calls to the top of the module (before any imports).

Mocking methods which are not implemented in JSDOM

If some code is using a method which JSDOM (the DOM implementation used by Jest) hasn't implemented yet, it is not easily possible to test it. This is the case with window.matchMedia() for example. Jest will return TypeError: window.matchMedia is not a function and doesn't execute the test properly.

In this case, mocking matchMedia in the test file should solve this issue:

window.matchMedia = jest.fn().mockImplementation(query => {
  return {
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(), // deprecated
    removeListener: jest.fn(), // deprecated
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  };
});

This will work if window.matchMedia() is used in a function (or method) which is invoked in the test. If you execute window.matchMedia() directly in the tested file, Jest will report the same error. In cases as this, the solution will be to move the manual mock into a separate file and include this one in the test before testing the file:

import './matchMedia.mock'; // Must be imported before the tested file
import {myMethod} from './file-to-test';

describe('myMethod()', () => {
  // Test the method here...
});

Previous: Snapshot Testing with Jest.
Next: Using Timer Mocks in Jest.



Become a Patron!

Follow us on Facebook and Twitter for latest update.

It will be nice if you may share this link in any developer community or anywhere else, from where other developers may find this content. Thanks.

https://www.w3resource.com/jest/manual-mocks.php