jest ReferenceError: Cannot access '' before initialization

Viewed 35491

I'm getting the error:

ReferenceError: Cannot access 'myMock' before initialization

Even though i respected jest documentation about the hoisting: A limitation with the factory parameter is that, since calls to jest.mock() are hoisted to the top of the file, it's not possible to first define a variable and then use it in the factory. An exception is made for variables that start with the word 'mock'.

I'm doing this:

import MyClass from './my_class';
import * as anotherClass from './another_class';

const mockMethod1 = jest.fn();
const mockMethod2 = jest.fn();
jest.mock('./my_class', () => {
  return {
    default: {
      staticMethod: jest.fn().mockReturnValue(
        {
          method1: mockMethod1,
          method2: mockMethod2,
        })
    }
  }
});

as you can see both of my variables respect the "standard" but are not hoisted properly.

Am I missing something ?

Obviously it works when I just pass jest.fn() instead of my variables, but i'm not sure how to be able to use these in my test later on.

6 Answers

None of the answers above solved my problem, so here's my solution:

var mockMyMethod: jest.Mock;

jest.mock('some-package', () => ({
  myMethod: mockMyMethod
}));

Something about using const before the imports feels weird to me. The thing is: jest.mock is hoisted. To be able to use a variable before it you need to use var, because it is hoisted as well. It doesn't work with let and const because they aren't.

The accepted answer does not handle when you need to spy on the const declaration, as it is defined inside the module factory scope.

For me, the module factory needs to be above any import statement that eventually imports the thing you want to mock. Here is a code snippet using a nestjs with prisma library.

// app.e2e.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import mockPrismaClient from './utils/mockPrismaClient'; // you can assert, spy, etc. on this object in your test suites.

// must define this above the `AppModule` import, otherwise the ReferenceError is raised.
jest.mock('@prisma/client', () => {
  return {
    PrismaClient: jest.fn().mockImplementation(() => mockPrismaClient),
  };
});

import { AppModule } from './../src/app.module'; // somwhere here, the prisma is imported

describe('AppController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });
)};

To clarify what Jason Limantoro said, move the const above where the module is imported:

const mockMethod1 = jest.fn(); // Defined here before import.
const mockMethod2 = jest.fn();

import MyClass from './my_class'; // Imported here.
import * as anotherClass from './another_class';

jest.mock('./my_class', () => {
  return {
    default: {
      staticMethod: jest.fn().mockReturnValue(
        {
          method1: mockMethod1,
          method2: mockMethod2,
        })
    }
  }
});

The problem that the documentation addresses is that jest.mock is hoisted but const declaration is not. This results in factory function being evaluated at the time when mocked module is imported and a variable being in temporal dead zone.

If it's necessary to access nested mocked functions, they need to be exposed as a part of export object:

jest.mock('./my_class', () => {
  const mockMethod1 = jest.fn();
  const mockMethod2 = jest.fn();
  return {
    __esModule: true,
    mockMethod1,
    mockMethod2,
    default: {
      ...

This also applies to manual mocks in __mocks__ where variables are accessible inside a mock only.

You can use doMock to avoid hoisting behaviour. If this happens on the top of your file, it should be a 1 to 1 change.

const mockMyMethod = jest.fn();
jest.doMock('some-package', () => ({ myMethod: mockMyMethod }));

This solution works for me and it's pretty easy for vuejs+ jest.

Two points to note:

  • you should declare the absolute path and not '@/js/network/repositories'
  • the getter helps to defer the instantiation
    const mockGetNextStatuses = jest.fn();
    const mockUpdatePrintingStatus = jest.fn();
    
    jest.mock('../../../../../../src/js/network/repositories/index.js', () => {
        return {
            get printing() {
                return {
                    getNextStatuses: mockGetNextStatuses,
                    updatePrintingStatus: mockUpdatePrintingStatus,
                }
            }
        }
    });

or

jest.mock('../../../../../../src/js/network/repositories/index.js', () => ({
    printing: {
        getNextStatuses: jest.fn(),
        updatePrintingStatus: jest.fn()
    }
}));
import { printing } from '../../../../../../src/js/network/repositories/index.js';


// and mock the module
printing.getNextStatuses.mockReturnValue(['XX','YY']);
Related