Partially Mocking an ES Module with Jest and Node.js (2024)

Disclaimer: This article has nothing to do with C++. Also, I do not have a huge amount of experience with the Node.js ecosystem. This information is provided here because of the issues I had reading outdated, and often incorrect, “solutions” on forums and blogs. I hope someone finds it useful.

Suppose you have a database.js module which interacts with a database server to query and update users. This is abstracted away from the client user.js module, which is what your Node.js/Express app communicates with. Now suppose you want to write unit tests for user.js without accessing the real database. You could create a “testing” database, but a better way may be to “mock” database.js. This is where the testing framework imports user.js, while providing its own functions which override those in database.js for the duration of the test suite.

I’m not going to try to describe all of Jest here, and I’m assuming also that the reader is familiar with ESM and the import keyword together with both named and default exports. The method which works for me is as follows:

  • Create a test file user.test.js which imports the Jest framework
  • Import the module you want to mock (database.js) dynamically with await import()
  • Redefine the exports as desired within jest.unstable_mockModule()
  • Import the module you want to unit-test (user.js), again dynamically
  • Write the tests with test('what it tests', async () => { test here... });
  • Create a suitable package.json and jest.config.json
  • Set environment variable (important – still necessary with v21.6): NODE_OPTIONS=--experimental-vm-modules
  • Run npm test in the project directory

Here is the sample user.test.js demonstrating the above:

import { jest } from '@jest/globals';const mockModule = await import('./database.js');jest.unstable_mockModule('./database.js', () => ({ ...mockModule, default: jest.fn(() => 1), fetchFromDatabase: jest.fn().mockResolvedValue({ id: 42, name: 'John Doe', email: '[email protected]' }),}));const { getUser, makeUser, getTotalUsers } = await import('./user.js');test('getUser() returns correct name and email', async () => { const user = await getUser(1); expect(user.user).toBe('John Doe'); expect(user.email).toBe('[email protected]');});test('getTotalUsers() returns correct number', async () => { const numberOfUsers = getTotalUsers(); expect(numberOfUsers).toBe(1);});test('makeUser() is callable', async() => { const newUser = await makeUser('Jane Doe', '[email protected]'); expect(newUser).toBe(undefined);});

Line 6 is the spread operator which imports all of the exports of database.js into the mock. Line 7 redefines the default export getNumberOfUsers() to always return 1. Line 8 defines the result of fetchFromDatabase() (which is a Promise as it is an async function) to always be resolved as a dummy user. (Should mocking all the exports be required, lines 3 and 6 are not needed.)

Here is user.js:

import getNumberOfUsers, { fetchFromDatabase, generateUserId } from './database.js';export async function getUser(userId) { const user = await fetchFromDatabase(userId); return { user: user.name, email: user.email };}export async function makeUser(user, email) { const newUser = { id: generateUserId(), user: user, email: email }; // store this somehow return newUser;}export function getTotalUsers() { return getNumberOfUsers();}

And here is the dummy database.js:

const db_credentials = { username: 'dbuser', password: '$$hashed_password$$' };// We want to mock this functionexport async function fetchFromDatabase(id) { // Query database here return [{ id: 1, name: 'Alice', email: '[email protected]' }, { id: 2, name: 'Bob', email: '[email protected]' }, { id: 3, name: 'Charlie', email: '[email protected]' }][id];}// We don't want to mock this functionexport function generateUserId() { const date = new Date(); return date.getTime();}// This is the default export, which we do want to mockfunction getNumberOfUsers() { // Query database here return 3;}export default getNumberOfUsers;

Finally a minimalist jest.config.json which prevents harmful rewriting of our test module:

{ "transform": {}}

And a package.json setting the necessary options:

{ "type": "module", "scripts": { "test": "jest" }}

Now you can run (Windows):

set NODE_OPTIONS=--experimental-vm-modulesnpm test

Or (everything else):

NODE_OPTIONS=--experimental-vm-modules npm test

The output should hopefully look something like:

Finally, the source is available to download from here (as a .zip), and the version of Jest used was 29.7.0. (Node.js latest stable v21.6.1 and npm 10.2.4). This process is likely to change, so if you are writing tests for a larger project and don’t want them to break in the future, you may wish to install Jest as a development dependency with npm install --save-dev jest, which will install Jest in a node_modules folder within your project.

If you have any issues or additions to make to the above, please leave a comment; I shall try to keep this article updated as support for ESM with Jest matures.

Partially Mocking an ES Module with Jest and Node.js (2024)

FAQs

How to mock a module using jest? ›

Mocking user modules​

Manual mocks are defined by writing a module in a __mocks__/ subdirectory immediately adjacent to the module. For example, to mock a module called user in the models directory, create a file called user. js and put it in the models/__mocks__ directory.

What are the best practices for mocking jest? ›

Best Practices for Mocking with Jest

When using jest mocks, it's important to follow best practices to ensure your tests are reliable and maintainable: Always clear and reset your mocks between tests to avoid shared state. Use mock implementations that closely mimic the behavior of the real functions or modules.

How to mock a function in jest nodejs? ›

You can create a mock function with jest.fn() . If no implementation is given, the mock function will return undefined when invoked.

How to mock a model in jest? ›

To mock an object in Jest, use the jest. mock() function with the path to the module you want to mock. You can then define a mock implementation for the object's methods and properties using jest. fn().

How can I mock an ES6 module import using jest? ›

Calling jest.mock() with the module factory parameter

jest. mock(path, moduleFactory) takes a module factory argument. A module factory is a function that returns the mock. In order to mock a constructor function, the module factory must return a constructor function.

How to mock a function call inside a module? ›

There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.

Can I mock a variable in Jest? ›

fn. When you need to mock a single function that is the value of a variable—e.g., a const or an argument to another function —use jest. fn .

How to mock a component in Jest? ›

First, to mock a component, you use jest. mock("path/to/RealComponent") . You can specify an mock implementation inline like jest. mock("../src/Icon" () => { ... }) .

What is an example of Jest? ›

Basic Jest Examples

Here's an example that tests a simple function: function sum(a, b) { return a + b; } test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3); }); This example defines a sum function and a test that checks if the function returns the expected result.

How do you mock an object of a class in Jest? ›

Mocking a JavaScript Class in Jest

There are multiple ways to mock an ES6 class in Jest. To keep things simple and consistent you will use the module factory parameters method and jest SpyOn to mock specific method(s) of a class. These two methods are not only flexible but also maintainable down the line.

How to check if a function is called in Jest? ›

To test if a function is called based on a condition in Jest, you can use Jest's built-in mock functions and assertions. Here is an example: In this example, we create a mock function using Jest's jest. fn() method.

How to mock a native module in jest? ›

Create a file with the same name as the native module you want to mock and place it in your module's mocks directory. Make sure to export the mock implementation from this file. The jest-expo preset will automatically return the exported functions because of a requireNativeModule call when running during a unit test.

How do you mock a module in React? ›

To mock a module with both, you can use the default key, but you also must specify that it's an ES6 module with a special __esModule key: export { A }; export default B; jest. mock("../src/Icon", () => { return { __esModule: true, A: true, default: () => { return <div></div>; }, }; });

How to mock a component in jest? ›

To mock a React component within Jest you should use the `jest. mock` function. The file that exports the specific component is mocked and replaced with a custom implementation. Since a component is essentially a function, the mock should also return a function.

Can I mock a variable in jest? ›

fn. When you need to mock a single function that is the value of a variable—e.g., a const or an argument to another function —use jest. fn .

Top Articles
Latest Posts
Article information

Author: Greg O'Connell

Last Updated:

Views: 6310

Rating: 4.1 / 5 (62 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: Greg O'Connell

Birthday: 1992-01-10

Address: Suite 517 2436 Jefferey Pass, Shanitaside, UT 27519

Phone: +2614651609714

Job: Education Developer

Hobby: Cooking, Gambling, Pottery, Shooting, Baseball, Singing, Snowboarding

Introduction: My name is Greg O'Connell, I am a delightful, colorful, talented, kind, lively, modern, tender person who loves writing and wants to share my knowledge and understanding with you.