Barry Piccinni
Barry Piccinni

Reputation: 1801

Jest mocking a function within another function

I have searched quite a bit, and whilst there are many questions asked about mocking functions, none of the solutions I've found work for my setup, and I'm not quite sure why. I have the following:

questionnaire-service.js

const service = require('./request-service')();

function questionnaireService() {
    function createQuestionnaire() {
        const opts = {
            url: `http://some-url.com`,
            body: {
                data: "some-data"
            }
        };
        return service.post(opts);
    }

    return Object.freeze({
        createQuestionnaire
    });
}

module.exports = questionnaireService;

request-service.js

const got = require('got');
const merge = require('lodash.merge');

function requestService() {
    function post(options) {
        let opts = {
            method: 'POST',
            headers: {
                accept: 'application/json',
                'Content-Type': 'application/json'
            },
            json: true,
            body: options.body
        };
        opts = merge(opts, options);
        return got(opts.url, opts);
    }

    return Object.freeze({
        post
    });
}

module.exports = requestService;

I am trying to write tests for the questionnaire service, and want to mock the 'post' function. I have tried the following

questionnaire-service.test.js

const requestService = require('./request-service')();
const questionnaireService = require('./questionnaire-service')();
const createdQuestionnaire = require('./test-fixtures/res/get_questionnaire.json');

describe('questionnaire service routes', () => {
    it('Should create a new questionnaire', async () => {
        const spy = jest.spyOn(requestService.post);
        spy.mockReturnValue(createdQuestionnaire);

        const response = await questionnaireService.createQuestionnaire();

        expect(requestService.post).toBeCalled();
        expect(response).toMatch(createdQuestionnaire);
    }

    it('Should create a new questionnaire', async () => {
        jest.doMock('./questionnaire-service', () =>
            jest.fn(() => ({
                createQuestionnaire: () => createdQuestionnaire
            }))
        );

        const response = await questionnaireService.createQuestionnaire();

        expect(questionnaireService.createQuestionnaire).toBeCalled();
        expect(response).toMatch(createdQuestionnaire);
    }

    it('Should create a new questionnaire', async () => {
        jest.doMock('./request-service', () =>
            jest.fn(() => ({
                post: () => createdQuestionnaire
            }))
        );

        const response = await questionnaireService.createQuestionnaire();

        expect(requestService.post).toBeCalled();
        expect(response).toMatch(createdQuestionnaire);
    }
});

All of the above yield the same error: RequestError: getaddrinfo ENOTFOUND some_token some_token:443 which sounds like it's being thrown by the 'GOT' module not finding a url to hit. Can someone shed some light on how to get this to work correctly?

Upvotes: 0

Views: 933

Answers (1)

iofjuupasli
iofjuupasli

Reputation: 3873

When you make a require("somemodule") it loads this module and executes it, so it executes require calls inside this module. Then all the modules are cached, so whenever you make a require again, it doesn't execute again, and returns already cached dependencies.

When you do doMock, you need to reset this cache, and make new require in your test for everything, that depends on mocked module.

    jest.doMock('./questionnaire-service', () =>
        jest.fn(() => ({
            createQuestionnaire: () => createdQuestionnaire
        }))
    );
    jest.resetModules();
    const questionnaireService = require(`questionnaire-service`)();

    const response = await questionnaireService.createQuestionnaire();

Upvotes: 1

Related Questions