jest.mock(..) not working in 'describe' (TypeError: moduleName.split is not a function) - jestjs

jest.mock(..) does not seem to work at the 'describe' level for my tests.
If I have the following :
import React from 'react';
import {someFunction} from "./something/someFile";
describe('Overview Test', () => {
jest.mock(someFunction);
test(' snapshot', () => {
});
});
Then running the 'test' (ie. at the test level), works fine.
But if I run the 'describe' (ie. the describe level or suite level), then I get the following error :
TypeError: moduleName.split is not a function
at Resolver.resolveModuleFromDirIfExists (A:\frontend\node_modules\jest-resolve\build\index.js:224:30)
at Resolver.resolveModule (A:\frontend\node_modules\jest-resolve\build\index.js:252:12)
If I have this :
describe('Overview Test', () => {
test(' snapshot', () => {
jest.mock(someFunction);
});
});
Then both ways it does not work.
I have also tried this :
import React from 'react';
import {someFunction} from "./something/someFile";
describe('Overview Test', () => {
beforeEach(() => {
jest.mock(someFunction);
});
test(' snapshot', () => {
});
});
And it does not work.
UPDATE
I have also tried this and it does not work :
import React from 'react';
import {someFunction} from "./something/someFile";
describe('Overview Test', () => {
jest.mock('./something/someFile', () => {
return { someFunction: jest.fn(() => "futhissit")};
});
test(' snapshot', () => {
someFunction()
});
});

Jest mock is for mocking modules and the first argument is the moduleName which it has to be a valid module name (inside node_modules or a file path) and not a direct function/module:
jest.mock(moduleName, factory, options)
Mocks a module with an auto-mocked version when it is being required. factory and options are optional.
The error you're getting TypeError: moduleName.split is not a function is because resolveModuleFromDirIfExists tries to split the module name/path and you can see it inside jest-resolve/src/index.ts at line 207.
When you want to test an ES module, you pass the module location for the moduleName and you create a factory using __esModule: true and then create properties with exported functions being mocked using jest.fn():
someFile.js exports the someFunction:
module.exports.someFunction = () => 'Some function result!';
Mocking someFile.js module using jest.mock()
describe('Overview Test', () => {
// Mock the module and its functions
jest.mock('./someFile', () => ({
__esModule: true,
someFunction: jest.fn(() => 'Mocked someFunction!')
}));
// Import the function from the mocked module
const { someFunction } = require('./someFile');
test('snapshot', () => {
// Execute the mocked function
const someResult = someFunction();
// Expect to return the mocked value
expect(someResult).toBe('Mocked someFunction!');
});
});
You have to import the mocked modules after the jest.mock module mocking. You can create a jest.setup.js and configure it using setupFilesAfterEnv that can have your mocks inside it and then just import the modules like normal at the top of the test files.

Related

How can I mock a function called by the function I am exercising? The functions are in the same file [duplicate]

I have one file called helper.js that consist of two functions
export const funcA = (key) => {
return funcB(key)
};
export const funcB = (key,prop) => {
return someObj;
};
I have my helper.spec.js to test the helper.js file functions.
import {funcA,funcB} from 'helper';
describe('helper', () => {
test('testFuncB', () => {
}
test('testFuncA', () => {
}
}
The test for funcB is pretty simple i just call it and expect someObj
The problem is to test funcA, in order to test it i want to mock the response of funcB.
I want testFuncB call the actual funcB and testFuncA call mocked funcB
How can i achieve funcB to be mocked and original in my two tests?
This is not a duplicate. It is a different case: they mock inner called functions only, if I remove the testFuncB then it will be the same but I must perform test on testFuncB too.
If an ES6 module directly exports two functions (not within a class, object, etc., just directly exports the functions like in the question) and one directly calls the other, then that call cannot be mocked.
In this case, funcB cannot be mocked within funcA the way the code is currently written.
A mock replaces the module export for funcB, but funcA doesn't call the module export for funcB, it just calls funcB directly.
Mocking funcB within funcA requires that funcA call the module export for funcB.
That can be done in one of two ways:
Move funcB to its own module
funcB.js
export const funcB = () => {
return 'original';
};
helper.js
import { funcB } from './funcB';
export const funcA = () => {
return funcB();
};
helper.spec.js
import * as funcBModule from './funcB';
import { funcA } from './helper';
describe('helper', () => {
test('test funcB', () => {
expect(funcBModule.funcB()).toBe('original'); // Success!
});
test('test funcA', () => {
const spy = jest.spyOn(funcBModule, 'funcB');
spy.mockReturnValue('mocked');
expect(funcA()).toBe('mocked'); // Success!
spy.mockRestore();
});
});
Import the module into itself
"ES6 modules support cyclic dependencies automatically" so it is perfectly valid to import a module into itself so that functions within the module can call the module export for other functions in the module:
helper.js
import * as helper from './helper';
export const funcA = () => {
return helper.funcB();
};
export const funcB = () => {
return 'original';
};
helper.spec.js
import * as helper from './helper';
describe('helper', () => {
test('test funcB', () => {
expect(helper.funcB()).toBe('original'); // Success!
});
test('test funcA', () => {
const spy = jest.spyOn(helper, 'funcB');
spy.mockReturnValue('mocked');
expect(helper.funcA()).toBe('mocked'); // Success!
spy.mockRestore();
});
});
Late answer but this should work.
Also you should test funcB in its own file and not inside the 'helper' tests.
import { funcB } from './funcB';
import { funcA } from './helper';
jest.mock('./funcB');
describe('helper', () => {
test('test funcA', () => {
const funcBSpy = jest.fn();
funcB.mockImplementation(() => funcBSpy());
funcA();
expect(funcBSpy).toHaveBeenCalledTimes(1);
});
});
import * as helper from 'helper';
describe('helper', () => {
it('should test testFuncA', () => {
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
mockTestFuncB.spyOn(helper, 'testFuncB').mockReturnValue(/*your expected return value*/);
// test logic
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
}
}
I create a kind of nameSpace to handle this issue:
let helper = {}
const funcA = (key) => {
return helper.funcB(key)
};
const funcB = (key,prop) => {
return someObj;
};
helper = { funcA, funcB }
module.exports = helper
and then mocking is obvious with jest.fn
You can use babel-plugin-rewire provided __set__ function to mock internal function.
Assuming you have set up babel-plugin-rewire.
helper.spec.js
import {funcA, __set__} as helper from './helper';
describe('helper', () => {
test('test funcA', () => {
__set__('funcB', () => {
return 'funcB return value'
})
expect(funcA()).toBe('funcB return value');
});
});
One advantage of this solution is you don't need to change any original code
I was able to get this working. I separated my helper and my main logic into two files like other solutions. In the test file, I had to mock the entire helper file.
const { doAdd } = require('./addHelper');
function add(a, b) {
return doAdd(a, b);
}
jest.mock('./addHelper');
// ...
it('should call doAdd', () => {
// hook into doAdd helper method and intercept its return value
jest.spyOn(helperModule, 'doAdd').mockReturnValue(11);
expect(addModule.add()).toEqual(11);
expect(helperModule.doAdd).toBeCalled();
});
Here is my solution:
https://github.com/davidholyko/jest-sandbox
You can do the following trick when you test the funcA:
1.Mock the funcB:
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
2.Change the funcB(key) to this.funcB(key)
I had the same problem and worked! Full Code:
export const funcA = (key) => {
return this.funcB(key)
};
export const funcB = (key,prop) => {
return someObj;
};
Test Code:
import helper from 'helper';
describe('helper', () => {
test('testFuncB', () => {
...
}
test('testFuncA', () => {
helper.funcB = jest.fn().mockImplementationOnce(() => <your data>);
}
}
I think this might work
import * as helper from 'helper';
describe('helper', () => {
test('testFuncB', () => {
}
test('testFuncA', () => {
const mockTestFuncB = jest.mock();
// spy on calls to testFuncB and respond with a mock function
jest.spyOn(helper, 'testFuncB').mockImplementationOnce(mockTestFuncB);
// Do the testing ...
// Restore helper.testFuncB to it's original function
helper.testFuncB.mockRestore();
}
}

How to test a function in React Component with Jest + Enzyme?

I am using the testing-library/jest-dom + Enzyme to do the unit testing.and here I need to test a funtion inside a React function.
import React, { useEffect } from 'react';
function Component1() {
useEffect( async () => {
fetchSomeData();
}, [])
const fetchSomeData = async () =>{
console.log('fetchSomeData')
}
return <div>Component1</div>;
}
export default Component1;
and my test file:
Enzyme.configure({adapter:new Adapter()})
describe('Name of the group', () => {
it('should ', () => {
const wrapper = shallow( <Component1/>)
expect(wrapper.exists());
});
});
in fact I don't need to test the correctness , I just need the code be covered in jest. but whatever I tried , it seems not covered by jest:

Testing if external component method is called in jest

I am using jest and enzyme for unit testing. Below is my index.js file. I need to test openNotification and uploadErrorNotification function of the file. However, only uploadErrorNotification function is exported. So, How do I test both the functions.
Also, I don't want to use any other libray except jest and enzyme.
//index.js
import {
notification
} from 'antd';
const openNotification = (message, description, className) => {
notification.open({
key: 'upload-template',
message,
description,
placement: "bottomRight",
duration: null,
});
};
const uploadErrorNotification = (uploadFailedText, errorMsg) => {
openNotification(uploadFailedText, errorMsg, 'error');
};
export {
uploadErrorNotification
}
This is my test file:
//test.js
import { uploadErrorNotification } from '../index.js
jest.mock('notification', () => ({ open: () => jest.fn() })); // was trying this but I couldn't understand how it will work
describe('Notification validation functions testing', () => {
uploadErrorNotification('Upload failed', 'Something went wrong.');
expect("openNotification").toHaveBeenCalledTimes(1); // want to do something like this
});
You have to mock the external depenency:
first mock antd so that notification.open is a spy
jest.mock('antd', () => ({notification: open: {jest.fn()}}))
Then import the module into your test
import { notification } from 'antd';
Know you can use it like this:
expect(notification.open).toHaveBeenCalledTimes(1);
If you want to test notification without overwrite other antd component, you can add jest.requireActual('antd').
jest.mock('antd', () => {
return {
...jest.requireActual('antd'),
notification: {
open: jest.fn(),
},
};
});

How to test a module in NestJs

I'm adding tests on a project and improving coverage. I would like to know how can I test a module definition (mymodule.module.ts file) in NestJs.
In particular, I'm testing a main module that imports other modules, one of them inits a db connection, this means that I need to mock the service on the other module, to avoid a real db connection.
At this moment I have something like this:
beforeEach(async () => {
const instance: express.Application = express();
module = await NestFactory.create(MyModule, instance);
});
describe('module bootstrap', () => {
it('should initialize entities successfully', async () => {
controller = module.get(MyController);
...
expect(controller instanceof MyController).toBeTruthy();
});
});
This works, but I'm sure this can get improvements :)
The ideal thing would be something like overrideComponent method provided by Test.createTestingModule.
P.S: I'm using 4.5.2 version
Improved my tests based on #laurent-thiebault answer:
import { Test } from '#nestjs/testing';
import { ThingsModule } from './things.module';
import { ThingsResolver } from './things.resolver';
import { ThingsService } from './things.service';
describe('ThingsModule', () => {
it('should compile the module', async () => {
const module = await Test.createTestingModule({
imports: [ThingsModule],
}).compile();
expect(module).toBeDefined();
expect(module.get(ThingsResolver)).toBeInstanceOf(ThingsResolver);
expect(module.get(ThingsService)).toBeInstanceOf(ThingsService);
});
});
You can now test your module (at least for code coverage) by building it that way:
describe('MyController', () => {
let myController: MyController;
beforeEach(async () => {
const module = await Test.createTestingModule({
imports: [MyModule],
}).compile();
myController = module.get<MyController>(MyController);
});
it('should the correct value', () => {
expect(myController.<...>).toEqual(<...>);
});
});
We usually didn't test .module.ts files directly.
we do this in e2e testing.
but I wonder why one should test the the module ! and you are trying to test if the module can initialize it's components , it should.
but i recommend you do this in e2e testing.
in my opinion, in unit testing you should focus on testing the services or other components behaviors not the modules.

Mock.mockImplementation() not working

I have a service class
Service.js
class Service {
}
export default new Service();
And I am trying to provide a mock implementation for this. If I use something like this:
jest.mock('./Service', () => { ... my mock stuff });
It works fine, however I'm not able to access any variables declared outside of the mock, which is a bit limiting as I'd like to reconfigure what the mock returns, etc.
I tried this (inspired by this other StackOverflow article: Service mocked with Jest causes "The module factory of jest.mock() is not allowed to reference any out-of-scope variables" error)
import service from './Service';
jest.mock('./Service', () => jest.fn);
service.mockImplementation(() => {
return { ... mock stuff }
);
Unfortunately when I am trying to run this, I get the below error:
TypeError: _Service2.default.mockImplementation is not a function
I had same problem as #Janos, the other answers didn't help either. You could do two things :
If you need to mock only a function from Service, in your test file:
import service from './Service';
jest.mock('./Service', () => jest.fn());
service.yourFunction = jest.fn(() => { /*your mock*/ })
 
If you need to mock the entire Module:
Say your service.js is in javascript/utils, create a javascript/utils/_mocks_ and inside it create a service.js file, you can then mock the entire class in this file, eg:
const myObj = {foo: "bar"}
const myFunction1 = jest.fn(() => { return Promise.resolve(myObj) })
const myFunction2 = ...
module.exports = {
myFunction1,
myFunction2
}
then in your test file you just add:
jest.mock('./javascript/utils/service')
...functions exported from the mockfile will be then hit through your test file execution.
The mock is equal to jest.fn. You need to call jest.fn to create a mocked function.
So this:
jest.mock('./Service', () => jest.fn);
Should be:
jest.mock('./Service', () => jest.fn());
ran into similar issues and resolved it by using .mockImplementationOnce
jest.mock('./Service', () => jest.fn()
.mockImplementationOnce(() => {
return { ... mock stuff }
})
.mockImplementationOnce(() => {
return { ... mock other stuff }
})
);
now when you run another test it will return the second mock object.
You need to store your mocked component in a variable with a name prefixed by "mock" and make sure you return an object with a default property as you import your Service from the default in your "main.js" file.
// Service.js
class Service {
}
export default new Service();
// main.test.js (main.js contains "import Service from './Service';")
const mockService = () => jest.fn();
jest.mock('./Service', () => {
return {
default: mockService
}
});
I had similar problem, and the cause was that ".spec.js" file had an
import jest from "jest-mock";
After removing this line, it worked.
My mistake was that I was resetting the mock before each test. If you do that, be sure to reconfigure the mock implementation.
For example, change this:
let value;
let onPropertyChange: OnPropertyChangeCallback = jest.fn((changes: any) => {
value = changes["testValue"];
});
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
});
to this:
let value;
let onPropertyChange: OnPropertyChangeCallback;
const user = userEvent.setup();
beforeEach(() => {
jest.resetAllMocks();
onPropertyChange = jest.fn((changes: any) => {
value = changes["testValue"];
});
});

Resources