admin管理员组文章数量:1134241
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.
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.
13 Answers
Reset to default 107None of the answers above solved my problem, so here's my solution:
const mockMyMethod = jest.fn();
jest.mock('some-package', () => ({
myMethod: () => mockMyMethod()
}));
EDIT
The previous solution used hoisting the var
, but that's a weird work-around. The updated solution does so without hoisting in a way that jest is happy with. It works because the mockMyMethod
function is not actually executed until the myMethod
method is called from the mocked package.
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,
})
}
}
});
You should move your mocking above your imports; that could be the source of your issue. Imports are also hoisted, so multiple hoisted entries would be hoisted in order.
jest.mock('./my_class', () => {
const mockMethod = jest.fn()
const default = { staticMethod: jest.fn().mockReturnValue({ method: mockMethod }) };
return { default, mockMethod };
});
import MyClass, { mockMethod } from './my_class'; // will import your mock
import * as anotherClass from './another_class';
However, if you for some reason can't do that, you could use doMock
to avoid hoisting behaviour. If this happens on the top of your file, it should be a 1 to 1 change.
var mockMyMethod = jest.fn();
jest.doMock('some-package', () => ({ myMethod: mockMyMethod }));
I also recommend using var
instead of const
or let
for mocks. Their hoisting behaviour is slightly different, and I've found a few times that using var
seems to work better with jest's own hoisting.
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.
I got in the same problem.
ReferenceError: Cannot access 'mockYourFunction' before initialization
The selected answer works, but the explanation is wrong, it is not because the import order.
The reason it works is because the answer changed mockResturnValue
to mockImplementation
.
mockResturnValue
will try to resolve the value immediately while
mockImplementation
will do it only when been invoked, given the time needed to your variable be there when needed.
some, to exemplify a working solution, without reorder imports or add var and avoiding some fervent comments in your PR, here is the important part of the code:
const mockPrismaClient = jest.fn();
jest.mock('@prisma/client', () => {
return {
PrismaClient: jest.fn().mockImplementation(() => mockPrismaClient),
};
});
Per this article... Anyone making it to this page may just be able to set the mocked function to a function returning a function call. As per the OP's code:
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(), // <- function returning function call
method2: () => mockMethod2(),
})
}
}
});
None of these answers helped. Trying to use var kept giving me the TypeError: mockMyMethod is not a function. Yes I tried several function styles (delegate, function, etc).
What worked for me (disclaimer, this might not work with 3rd party packages, it might only work with your custom packages) :
import * as somePackage from 'some-package';
...
describe('desc', () => {
...
beforeEach(() => {
var mockMyMethod: jest.fn().mockResolvedValue({myMethod: whateverHere, etc: null});
jest.spyOn(somePackage, 'methodOrHookName').mockImplementation(mockMyMethod);
});
it(...)
it('testWithDifferentValue', () => {
var mockMyMethod: jest.fn().mockResolvedValue({myMethod: superDifferent, etc: 'etc'});
jest.spyOn(somePackage, 'methodOrHookName').mockImplementation(mockMyMethod);
// write test that is different to other tests.
});
}));
I just realized that it is because of how I was passing the mocked implementation. Here is how I was doing it:
Wrong code:
import { BaseQueryApi } from '@reduxjs/toolkit/query';
import { waitFor } from '@testing-library/react';
import axios from 'axios';
const mockWaitForUnlock = jest.fn();
const axiosMock = jest.mocked(axios);
jest.mock('axios');
jest.mock('async-mutex', () => ({
// Or: Mutex: jest.fn().mockImplementation(() => ({
Mutex: jest.fn(() => ({
waitForUnlock: mockWaitForUnlock,
})),
}));
Working solution
import { BaseQueryApi } from '@reduxjs/toolkit/query';
import { waitFor } from '@testing-library/react';
import axios from 'axios';
const mockWaitForUnlock = jest.fn();
const axiosMock = jest.mocked(axios);
jest.mock('axios');
jest.mock('async-mutex', () => ({
Mutex: jest.fn(() => ({
waitForUnlock: jest
.fn()
.mockImplementation(() => mockWaitForUnlock),
})),
}));
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']);
This solution worked for me:
jest.mock("../user/useCases/GetUserBalancesUseCase.js", () => {
return {
GetUserBalancesUseCase: {
create: jest.fn().mockReturnValue({
execute: jest
.fn()
.mockReturnValue(require("./fixtures/user/useCaseResponse.js").useCaseResponse),
}),
},
}
})
I couldn't use the fixture importing it using ESM syntax in this implementation. So I tried using require and fixed the import reference error issue.
This kind of mocking though, is not reliable because bypasses some logic that I want to have covered by the unit test. In my case is necessary to avoid interacting with the infrastructure.
As a reference, I need to test the output of an useCase given an input:
test("Get User Balances", async () => {
const useCaseResponse = await getUserBalancesUseCase.execute({ account, USDPrices })
expect(useCaseResponse).toEqual(getUserBalancesUseCaseResponse)
})
Jest mock method accepts a factory function in the second argument. so we have a hook named useMessage like this:
// hook file
const useMessage = () => {
return {
message: 'hi from useMessage hook'
}
}
Now in the code below, we are mocking our hook by returning a new hook made by jest.fn ( you can also do it without jest.fn )
// test file
jest.mock(`./useMessage`, () => {
return jest.fn(() => ({
message: 'hi from mocked useMessage',
}))
})
Example of using TypeScript with Jest and mockDebug.js module
jest.mock('debug', () => {
global.mockDebug = jest.fn();
return () => global.mockDebug;
});
// usage
describe('xxx', () => {
test('xxx', () => {
expect(global.mockDebug.mock.calls.toString()).toContain('ccc');
})
});
本文标签: javascriptjest ReferenceError Cannot access 3939 before initializationStack Overflow
版权声明:本文标题:javascript - jest ReferenceError: Cannot access '' before initialization - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736800401a1953465.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论