jest ReferenceError: Cannot access '' before initialization - javascript

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.

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 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.
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']);

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');
})
});

Related

Jest mock not being used when called from a constant being imported from another file

I have a method which is using a constant defined in another call. This constant in itself makes a service call that I have mocked. When resolving this constant from the other file the mocked function is not being called
//function i am trying in test.ts
export const testableFunction = async() => {
return EXPORTED_CONSTANT
}
//constant being exported from constants.ts
export const EXPORTED_CONSTANT = {
value : service.method()
}
// unit test I have written
it('message trigger for sendInteractiveWhatsapp', async () => {
jest.spyOn(service, 'method').mockImplementation(async () => {
return "some_value";
});
expect(await testableFunction()).toEqual({value: 'some_value'});
});
This test is failing because the mock value being return is undefined. i.e.
{} != {value: 'some_value'}
You can try mocking the constants.ts file at the top of your tests. That way you will be mocking any uses of that module and its contents while your tests run. Following is a sample from jest documentation.
import moduleName, {foo} from '../moduleName';
jest.mock('../moduleName', () => {
return {
__esModule: true,
default: jest.fn(() => 42),
foo: jest.fn(() => 43),
};
});
moduleName(); // Will return 42
foo(); // Will return 43
You can read more about different types of mocks here

Unable to mock a dependent function if its from the same module

I have been trying to debug why this is happening. I am unable to mock a dependent function if its from the same module as the function calling it. But I am able to overcome this if the mocked function is moved to a separate module which is different from the module of the function calling it.
Not working scenario
Module A (filename.ts)
export const callingFunction = () => {
//....statements
dependentFunction();
}
export const dependantFunction = () => {
//....statements
//resolve with something
}
filename.test.ts
import { callingFunction } from './fileName'
jest.mock('./fileName',() => ({
...jest.requireActuals('./fileName'),
dependentFunction: jest.fn().mockImplementation(/*....Mocked implementation*/)
})
test('...test case description...', () => {
const callingFunctionRespose: any = callingFunction();
expect(callingFunctionResponse).toEqual(/*....something.....*/);
});
The above mock does not override the dependentFunction exported by the fileName.ts module. Instead, when the exported function callingFunction() is called, it uses the implementation defined in the module. (Found this out by logging the function definitions.
But this behaviour is not observed when the dependant function is moved to it own separate module.
Working scenario
fileName.ts
import { dependentFunction } from './dependentFunctions'
export const callingFunction = () => {
//....statements
dependentFunction();
}
dependentFunctions.ts
export const dependantFunction = () => {
//....statements
//resolve with something
}
fileName.test.ts
import { callingFunction } from './fileName'
jest.mock('./dependentFunctions',() => ({
...jest.requireActuals('./dependentFunctions'),
dependentFunction: jest.fn().mockImplementation(/*....Mocked implementation*/)
})
test('...test case description...', () => {
const callingFunctionRespose: any = callingFunction();
expect(callingFunctionResponse).toEqual(/*....something.....*/);
});
You can import the functions as a module
import * as module from './fileName'
To mock the implementation you can do
jest.spyOn(module, 'dependentFunction').mockImplementation(/*....Mocked implementation*/)
To call the other function, use
module.callingFunction()
Appologies for the late update. But the solution in the following article was the fix:
https://medium.com/welldone-software/jest-how-to-mock-a-function-call-inside-a-module-21c05c57a39f

Mocking classes in Jest does not call the same method

I'm trying to mock a class that is being imported into my code with require and then testing if a method of that class is getting called.
I've created a sample setup where this issue can be replicated:
// user.js
class User {
getName() {
return "Han Solo"
}
}
module.exports = User
// user-consumer.js
const User = require('./user')
const user = new User()
module.exports.getUserName = () => {
// do things here
return user.getName()
}
// user.test.js
const userConsumer = require('./user-consumer')
const User = require('./user')
jest.mock('./user')
it('should mock', () => {
const user = new User()
jest.spyOn(user, 'getName')
userConsumer.getUserName()
expect(user.getName).toBeCalled()
})
The error I get is as follows:
If I used ES6 syntax this would work as shown on jest's documentation: https://jestjs.io/docs/en/es6-class-mocks
But I unfortunately can't use ES6 on this project as it would require a lot of refactoring.
I also tried mocking the class with the module factory parameter
jest.mock('./user', () => {
return jest.fn(() => {
return {
getName: jest.fn(),
}
})
})
It still doesn't work. When I log console.log(user.getName) in user-consumer.js:5 it does show that the method has been mocked but whatever is called in user.getName() is not the consumer function still returns "Han Solo".
I've also tried it with and without jest.spyOn and it still returns the same error.
Is this just not possible with none ES6 syntax?
The problem is that Jest spies have undocumented behaviour.
Even if prototype method is the same for all instances:
new User().getName === new User().getName
A spy is specific to an instance:
jest.spyOn(new User(), 'getName') !== jest.spyOn(new User(), 'getName')
If a specific instance is unreachable, it's a prototype that needs to be spied:
jest.spyOn(User.prototype, 'getName')
userConsumer.getUserName()
expect(User.prototype.getName).toBeCalled();
A problem with jest.mock isn't specific to ES6 syntax. In order for a spy to be available for assertions and implementation changes, it should be exposed somewhere. Declaring it outside jest.mock factory is not a good solution as it can often result in race condition described in the manual; there will be one in this case too. A more safe approach is to expose a reference as a part of module mock.
It would be more straightforward for ES module because this way class export is kept separately:
import MockedUser, { mockGetName } from './user';
jest.mock('./user', () => {
const mockGetName = jest.fn();
return {
__esModule: true,
mockGetName,
default: jest.fn(() => {
return {
getName: mockGetName
}
})
}
})
...
For CommonJS module with class (function) export, it will be efficiently exposed as class static method:
import MockedUser from './user';
jest.mock('./user', () => {
const mockGetName = jest.fn();
return Object.assign(
jest.fn(() => {
return {
getName: mockGetName
}
}),
{ mockGetName }
})
})
...
MockedUser.mockGetName.mockImplementation(...);
userConsumer.getUserName()
expect(MockedUser.mockGetName).toBeCalled();

ReferenceError: Cannot access 'mockMethod1' before initialization

I have 3 source files File1.ts, File2.ts, File3.ts. While executing the Unit tests of File3 I am getting the following Error.
Test suite failed to run
ReferenceError: Cannot access 'mockMethod1' before initialization
20 | __esModule: true,
21 | default: jest.fn(),
> 22 | method1: mockMethod1,
| ^
23 | method2: mockMethod2
24 | }));
25 |
Here are the contents of the 3 source files and unit tests for File3.
File1.ts
export default class File1 {
public element;
constructor(element) {
this.element = element;
}
method1(inputs) {
// Logic of Method1.
return output;
}
method2(inputs) {
// Logic of Method2.
return output;
}
}
File2.ts
import File1 from '../Folder1/File1'
export default class File2 {
public file1Object;
constructor(element) {
this.file1Object = new File1(element);
}
method1(inputs) {
// Logic of Method1.
let out = this.file1Object.method1(inputs);
// Logic of Method1.
return output;
}
method2(inputs) {
// Logic of Method2.
let out = this.file1Object.method2(inputs);
// Logic of Method2.
return output;
}
}
File3.ts
import File2 from '../Folder2/File2'
export default class File3 {
public file2Object;
constructor(element) {
this.file2Object = new File2(element);
}
method1(inputs) {
// Logic of Method1.
let out = this.file2Object.method1(inputs);
// Logic of Method1.
return output;
}
method2(inputs) {
// Logic of Method2.
let out = this.file2Object.method1(inputs);
// Logic of Method2.
return output;
}
}
File3.test.ts
import File3 from "./File3";
import File2 from "../Folder2/File2";
const mockMethod1 = jest.fn();
const mockMethod2 = jest.fn();
jest.mock('../Folder2/File2', () => ({
__esModule: true,
default: jest.fn(),
method1: mockMethod1,
method2: mockMethod2
}));
const file3Object = new File3(inputElement);
beforeEach(() => {
jest.clearAllMocks();
});
test('Method-1 Unit Test', () => {
mockMethod1.mockReturnValue(expectedOutput);
let observedOutput = file3Object.method1(inputs);
expect(observedOutput).toBe(expectedOutput);
})
test('Method-2 Unit Test', () => {
mockMethod2.mockReturnValue(expectedOutput);
let observedOutput = file3Object.method2(inputs);
expect(observedOutput).toBe(expectedOutput);
})
I am not sure where I am making the mistake so I am unable to resolve this error. Any suggestions to resolve this issue.
There are several things that are causing trouble. First, as mentioned in jest docs:
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'. It's up to you to guarantee that they will be initialized on time!
What that means is you need to move around the lines of code to make them look like this:
// First the mock functions
const mockMethod1 = jest.fn();
const mockMethod2 = jest.fn();
// Only then your imports & jest.mock calls
import File3 from "./File3";
import File2 from "../Folder2/File2";
jest.mock('../Folder2/File2', () => ({
// ...
}));
The second issue is that you are mocking File2 as if it was exporting method1 and method2, you are not mocking method1 and method2 of the File2 class! Take a look at 4 ways of mocking an ES6 class in jest docs.

Several mocks of one module in one file with jest

I have to functions for example a, b. Both of them are elements of one module, and they are re-exported in index.js. Function a invokes function b.
It all works if i use jest.mock on the top of the file, but if i want to specify different mock implementation of b function in every it block it doesn't work. Also i try'ed to use jest.doMock but it doesn't work as well.
a.js
import * as fromDependencies from '.'
export function(arg) {
return !fromDependencies && !arg;
}
b.js
export function b() {
//some code
return boolean;
}
index.js
export * from 'a.js',
export * from 'b.js'
testFile
import a from '../a.js';
describe('isGroupOverlaidTest', () => {
it('should return false', () => {
jest.mock('../.', () => ({
b: jest.fn(() => true);
}))
expect(a(true)).toBe(false);
});
it('should return true', function() {
jest.mock('../.', () => ({
b: jest.fn(() => false);
}))
expect(a(false)).toBe(false);
});
});
The results are fake, anyway i want to call my mocked function not the original one. When i have jest.mock in the top of the file it works but i can achieve just one mock for on file. Do mock does't work. I'll be really grateful if somebody can provide some example how i can resolve that ;).
You could use a spy instead of a mock. Since a exports a function instead of an object then you'd have to wrap it.
import a from '../a.js'
const aWrapped = { a }
describe('isGroupOverlaidTest', () => {
it('should return false', () => {
const mockA = jest.fn()
const spyedA jest.spyOn(aWrapped, a).mockReturnValue(mockA)
expect(spyedA(true)).toBe(false);
});
});
Something like that.

Categories

Resources