The ES6 module that I want to test looks as follows:
function privateFunction() {
...
}
export function publicFunction() {
... does something ...
privateFunction()
... does something else ...
}
I am using JEST for my unit tests and I am trying to find a way to test publicFunction
and avoiding the execution of privateFunction
by mocking it but I couldn't succeed in the mock attempt. Any idea?
You can't test private functions, and they're not supposed to be tested in isolation as they're supposed to be part of the internal implementation. If you want to test it in isolation, then export it. If you think it's private and should not be exported, then you need to test through your other code.
Mocking techniques should be applied to the external dependencies of the class and not to the class itself. If mocking of private methods is essential for testing our classes, it usually indicates a bad design.
There are two ways to mock functions: Either by creating a mock function to use in test code, or writing a manual mock to override a module dependency.
jest.mock () is called with one required argument - the import path of the module we're mocking. It can also be called with an optional second argument - the factory function for the mock. If the factory function is not provided, Jest will automock the imported module.
When testing JavaScript code using Jest, sometimes you may find yourself needing to mock a module. Whether it’s because the module or the functions it exports are irrelevant to the specific test, or because you need to stop something like an API request from trying to access an external resource, mocking is incredibly useful.
If you want to mock a private function, try to use the prototype. For example, you need to mock privateFunction of the following class: export class Module { public publicFunction () { // do something this.privateFunction (); // do something } private privateFunction () { // do something } }
The mockImplementation () method is called with the new implementation as its argument. The new implementation will then be used in place of the previous one when the mock is called. We can combine this with jest.mock () factory functions to create mocked modules that contain mocked functions.
I found a way to mock my private function by using the babel-plugin-rewire
module.
In package.json
I have the following:
"devDependencies": { ... "babel-plugin-rewire": "1.0.0-beta-5", "babel-jest": "18.0.0", ...
In .babel.rc
I have the following:
{ "presets": [ "es2015", "stage-0", "react" ], "env": { "test": { "plugins": [ "babel-plugin-rewire" ] } }, ...
At this point I was able to mock the private function:
import * as moduleToTest from './moduleToTest.js' describe('#publicFunction', () => { it('mocks private function', () => { moduleToTest.__Rewire__('privateFunction', () => { console.log('I am the mocked private function'); }) ... }) })
If you want to mock a private function, try to use the prototype. For example, you need to mock privateFunction of the following class:
export class Module {
public publicFunction() {
// do something
this.privateFunction();
// do something
}
private privateFunction() {
// do something
}
}
So you should use Module.prototype in the jest.spyOn function.
import { Module } from "./my-module";
describe('MyModule', () => {
it('tests public function', () => {
// Arrange
const module = new Module()
const myPrivateFunc = jest.spyOn(Module.prototype as any, 'privateFunction');
myPrivateFunc.mockImplementation(() => {});
// Act
module.publicFunction();
// Assert
expect(myPrivateFunc).toHaveBeenCalled();
});
});
There is no way through the nature of JavaScript. The function is bound to the scope of the module, so there is no way to know that this function exists from the outside, so no way to access the function and in the end no way to mock it.
Maybe more important, you should not test on the internals of the object under test but only the public API. Cause that is everything that counts. No one cares how stuff is done internally as long as the public API stays stable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With