How to Mock Private Functions With Jest

Is there any way to mock private functions with Jest?

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

testing private functions in typescript with jest

It looks like you are wanting to verify that handleError gets called when build runs.

Private methods are compiled to normal JavaScript prototype methods, so you can use the any type to let the spy creation pass through the TypeScript type checking.

Here is a highly simplified example:

class OrderBuilder {
public build() {
this.handleError()
}
private handleError() {
throw new Error('missing ... field in order')
}
}

describe('Order Builder', () => {
it('should test the handleError', () => {
const handleErrorSpy = jest.spyOn(OrderBuilder.prototype as any, 'handleError');
const orderBuilder = new OrderBuilder()
expect(() => orderBuilder.build()).toThrow('missing ... field in order'); // Success!
expect(handleErrorSpy).toHaveBeenCalled(); // Success!
});
});

How to mock a un-export (private) function inside user module using Jest

The workaround I adopted is not to mock the entire "private" function but only the "impossible" part of the functionality inside the “private” function. In this case, fetching the token from the remote service doGetSession and calling external API using Axios lib request method.

// Mocks
import { request } from "axios";
import { doGetSession } from "../utilities/auth/auth";

// Targets
import { requestUploadStatementFile } from "./api";

jest.mock("../utilities/auth/auth");
jest.mock("axios");

describe("requestUploadStatementFile", () => {
it("should fire request with correct reqeust configuration object", done => {
doGetSession.mockImplementationOnce(() => {
return Promise.resolve({ idToken: { jtwToken: "SAMPLE-TOKEN" } });
});

request.mockImplementationOnce(() => {
return Promise.resolve({ data: [] });
});

requestUploadStatementFile({}).then(transactions => {
const transactionsExpected = [];
const requestExpectedArgs = {
data: {},
headers: { Accept: "application/json", Authorization: undefined, "Content-Type": "application/json" },
method: "POST",
url: "https://*.*.amazonaws.com/api/upload"
};

expect(transactions).toEqual(transactionsExpected);
expect(request).toHaveBeenCalledTimes(1);
expect(request).toHaveBeenCalledWith(requestExpectedArgs);

done();
});
});
});

Thanks for the comment from @felixmosh.

It is considered a bad practice to mock private function. You should always mock only the outer layer of your app, usually public API

How to Mock private method in Class?

Thanks to @Andrei-Dragotoniu I ended up to mock the service instead.

const module: TestingModule = await Test.createTestingModule({
providers: [{
provide: OperationDocumentService,
useValue: {
findById: () => OperationDocumentMock,
},
}]
});

The private method is already called in another tested method.

How can I mock a private property in a class I'm trying to test in jest

Unless # hard privacy is used, private properties can be accessed outside a class at runtime, TypeScript access modifiers are applied only at compilation time.

Accessing private members in tests can be considered a reflection.

Visibility can be bypassed with bracket notation, which is the preferable option:

controllerInstance['stepperMotors'] = ...;

Or with Reflect API:

Reflect.set(controllerInstance, 'stepperMotors', ...);

Or by disabling type checks:

(controllerInstance as any).stepperMotors = ...;

Since private property is set with prototype method, another approach is to mock it. It's applicable if original initialization causes undesirable side effects and needs to be avoided. BotController.prototype.someMethod = jest.fn() should never be used in Jest as it cannot be automatically cleaned up and cross-contaminates tests. Instead it could be:

jest.spyOn(BotController.prototype, 'initalizeMotors').mockImplementation(function (this: BotController) {
this['stepperMotors'] = ...;
});
...
expect(controllerInstance['initalizeMotors']).toHaveBeenCalled();


Related Topics



Leave a reply



Submit