Rajkumar Somasundaram
Rajkumar Somasundaram

Reputation: 1270

Spying on a non-exported node.js function using jest not working as expected

I am trying to mock a non-exported function via 'jest' and 're-wire'.

Here I am trying to mock 'iAmBatman' (no-pun-intended) but it is not exported.

So I use rewire, which does it job well. But jest.mock doesn't work as expected.

Am I missing something here or Is there an easy way to achieve the same ?

The error message given by jest is :

Cannot spy the property because it is not a function; undefined given instead

service.js

function iAmBatman() {
    return "Its not who I am underneath";
}

function makeACall() {
    service.someServiceCall(req => {
        iAmBatman();
    });
    return "response";
}

module.export = {
    makeACall : makeACall;
}

jest.js

const services = require('./service');
const rewire = require('rewire');
const app = rewire('./service');
const generateDeepVoice = app.__get__('iAmBatman'); 

const mockDeepVoice = jest.spyOn(services, generateDeepVoice);

mockDeepVoice.mockImplementation(_ => {
    return "But what I do that defines me";
});

describe(`....', () => {
    test('....', done => {
        services.makeACall(response, () => {

        });
    });
})

Upvotes: 5

Views: 20619

Answers (1)

Rens Baardman
Rens Baardman

Reputation: 5091

It is not entirely clear what your goal is, but if you look at the documentation of jest.spyOn, you see that it takes a methodName as the second argument, not the method itself:

jest.spyOn(object, methodName)

This explains your error: you didn't give the function name, but the function itself. In this case, using jest.spyOn(services, 'iAmBatman') wouldn't work, since iAmBatman is not exported, and therefore services.iAmBatman is not defined.

Luckily, you don't need spyOn, as you can simply make a new mock function, and then inject that with rewire's __set__ as follows:

(note that I deleted the undefined service.someServiceCall in your first file, and fixed some typos and redundant imports)

// service.js

function iAmBatman() {
    return "Its not who I am underneath";
}

function makeACall() {
    return iAmBatman();
}

module.exports = {
    makeACall: makeACall
}
// service.test.js

const rewire = require('rewire');
const service = rewire('./service.js');

const mockDeepVoice = jest.fn(() => "But what I do that defines me")
service.__set__('iAmBatman', mockDeepVoice)

describe('service.js', () => {
    test('makeACall should call iAmBatman', () => {
        service.makeACall();
        expect(mockDeepVoice).toHaveBeenCalled();
    });
})

Another option would be to restructure your code with iAmBatman in a seperate module, and then mock the module import with Jest. See documentation of jest.mock.

Upvotes: 8

Related Questions