Skip to content

jest.mock() doesn't work well with NODE_PATH #3069

Closed
@gaearon

Description

@gaearon

I am reporting what I consider to be a bug.
Originally reported in facebook/create-react-app#1271.

Some people use NODE_PATH to enable "absolute imports" in their codebase. For example, running NODE_PATH=. npm test makes every file and folder in the top-level directory available as require('name') rather than require('./name').

Current behavior

This approach works fine with Jest, but the mocking behavior is a bit confusing. jest.mock() only works with it correctly when jest.mock() matches the require. In other words, jest.mock('./a') will only work for files that happen to require('./a') but not for the ones that require('a'). Conversely, jest.mock('a') will only work for files that do require('a') but not require('./a'). Since both are possible when one sets NODE_PATH, it can be tricky to figure out what went wrong, and why jest.mock doesn't always work.

Expected behavior

When NODE_PATH is used, and a is a file or folder in that directory, both jest.mock('a') and jest.mock('./a') work the same way, and mock both require('a') and require('./a') calls.

Repro

git clone https://github.com/gaearon/jest-mock-issue-repro.git
cd jest-mock-issue-repro
npm i
npm test

See that the test fails. Open index.test.js and index.js.

Observe that jest.mock('./a', () => 'goodbye, '); only works if there's a require('./a') in index.js, and jest.mock('a', () => 'goodbye, '); only works if there's a require('a') in index.js.

I would expect that both work in both cases, and are considered equivalent, because they ultimately resolved to the same module.

What Does Node Do?

One could argue that it's intentional behavior because ./a and a are different paths, and Node is known to treat different paths as different modules, so Jest should do the same.

However I don't think this is the case with NODE_PATH.
With code like this:

require('./a');
require('./A');

Node will indeed execute the module twice.

However, with NODE_PATH=. and calls like this:

require('a');
require('./a');

Node will still execute the module just once. This means that the module ID should be based on path after resolving, and I argue that jest.mock() should also treat them as the same module.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions