Skip to content

proof of concept for a bug in the jest hoisting functionality

Notifications You must be signed in to change notification settings

klocke-io/jestHoistingTest

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jest Hoisting Bug Proof of Concept for .cjs files

Setup Instructions

  1. Clone the repository:
    git clone git@github.com:klocke-io/jestHoistingTest.git
    cd jestHoistingTest
  2. Install dependencies:
    npm install
  3. Run specific test files: For the .js test: (Works as expected ✅)
    npx jest __tests__/index.spec.js
    For the .cjs test: (Dose not work as expected ❌)
    npx jest __tests__/index.spec.cjs
    For the manually hoisted .cjs test (Works as expected ✅):
    npx jest __tests__/index.manually-hoisted.cjs

Project and Code Explanation

This repository demonstrates a potential bug in Jest related to the hoisting of jest.mock when using .cjs file extension for test files.

  • lib/index.js: Exports a function businessMessage that returns 'foo bar'.

    // lib/index.js
    function businessMessage() {
      return 'foo bar';
    }
    module.exports = { businessMessage };
  • tests/index.spec.js: ✅ Standard Jest test in CommonJS, with jest.mock after the require. This works as expected because Jest hoists the mock.

    // __tests__/index.spec.js
    const { businessMessage } = require('../lib');
    jest.mock('../lib', () => ({
      businessMessage: jest.fn(() => 'mocked!'),
    }));
    
    test('should use the mocked implementation', () => {
      expect(businessMessage()).toBe('mocked!');
    });
  • tests/index.spec.cjs: ❌ Identical to the .js test, but with a .cjs extension. Here, jest.mock is not hoisted, so the real implementation is used instead of the mock.

    // __tests__/index.spec.cjs
    const { businessMessage } = require('../lib');
    jest.mock('../lib', () => ({
      businessMessage: jest.fn(() => 'mocked!'),
    }));
    
    test('should use the mocked implementation', () => {
      expect(businessMessage()).toBe('mocked!');
    });
    // This test will fail because jest.mock is not hoisted in .cjs files
  • tests/index.manually-hoisted.cjs: ✅ The mock is placed before the require statement, manually simulating hoisting. This test works as expected, even with the .cjs extension.

    // __tests__/index.manually-hoisted.cjs
    jest.mock('../lib', () => ({
      businessMessage: jest.fn(() => 'mocked!'),
    }));
    const { businessMessage } = require('../lib');
    
    test('should use the mocked implementation', () => {
      expect(businessMessage()).toBe('mocked!');
    });
  • jest.config.cjs: Configures Jest to pick up both .js and .cjs test files.

    // jest.config.cjs
    module.exports = {
      testMatch: [
        '**/__tests__/**/*.js',
        '**/__tests__/**/*.cjs',
      ],
    };

Key Point

  • Jest's automatic hoisting of jest.mock does not work in .cjs files, but works in .js files. Manual hoisting (placing jest.mock before require) is required for .cjs files.

Test Environment

  • Node.js version: v23.10.0
  • npm version: 10.9.2
  • npx version: 10.9.2
  • Jest version: 29.7.0
  • OS: macOS 15.5 (Darwin Kernel Version 24.5.0, arm64)

Command:

node --version
npm --version
npx --version
npx jest --version
uname -a
sw_vers

Output:

v23.10.0
10.9.2
10.9.2
29.7.0
Darwin LQ9357DK4K 24.5.0 Darwin Kernel Version 24.5.0: Tue Apr 22 19:53:27 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T6041 arm64
ProductName:            macOS
ProductVersion:         15.5
BuildVersion:           24F74

About

proof of concept for a bug in the jest hoisting functionality

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published