How to test a function passed as parameter (validate, filter) to inquirer.prompt in Jest Unit Testing?

546 views Asked by At

I'm trying to mock method inquirer.prompt in a unit testing, which has several questions (in the example I put only two examples).

return prompt([
{
    type: 'input',
    name: 'name1',
    message: 'Question 1?',
    filter: (fieldValue: string) => fieldValue.trim(),
    validate: (fieldValue: string) =>   showValidationResults(fieldValue, validateFieldIsNotEmpty),
},
{
    type: 'input',
    name: 'name2',
    message: 'Question 2?',
    validate: (fieldValue: string) =>   showValidationResults(fieldValue, validateFieldIsNotEmpty),
},
...
]);
    

I need to reach a certain unit test coverage so I want to check that the filter and validate functions are being correctly called.

I am only finding solutions that suggest to extract those functions to their own method and then call them directly from the unit test, but this is not a good solution for me because the unit test doesn't still know if the lines of filter and validate are being called, makes the code less clean.

Also, I would prefer not to call private methods directly from the unit test instead of calling the main method of the class.

Is there a way that I could mock prompt so I can inject the answers to the questions and then check that the filter and validate portions are executed?

1

There are 1 answers

0
mardo On BEST ANSWER

Inspired by the comment of @jonrsharpe I came out with the following solution:


const mockValidateFieldIsNotEmpty = jest.fn();
const mockPrompt = jest.fn();

import ExecutionEnvironmentContext from '../../../../ExecutionEnvironmentContext';

import ClassToTest from '../ClassToTest';

jest.mock('../../../../validation/InteractiveAnswersValidator', () => {
    const ActualInteractiveAnswersValidator = jest.requireActual('../../../../validation/InteractiveAnswersValidator');
    return {
        ...ActualInteractiveAnswersValidator,
        validateFieldHasNoSpaces: mockValidateFieldHasNoSpaces,
    };
});

jest.mock('inquirer', () => {
    return {
        Separator: jest.fn(),
        prompt: mockPrompt,
    };
});

describe('Class to test', () => {
    it('should call filter and validate methods of inquirer prompt', async () => {
        const expectedErrorMessages = ['errorMessage'];
        mockValidateFieldIsNotEmpty.mockReturnValue({ result: true });

        mockPrompt.mockImplementation((opt: { type: string; name: string; message: string; filter?: any; validate?: any }[]) => {
            const choice = opt[0];
            expect(opt[0]).toHaveProperty('filter');
            expect(opt[0]).toHaveProperty('validate');
            expect(opt[1]).toHaveProperty('validate');
            expect(opt[0].filter('  mockMessage  ')).toEqual('mockMessage');
            expect(opt[0].validate('  mockMessage  ')).toEqual(true);
            expect(opt[1].validate('mockMessage')).toEqual(true);
        });

        await ClassToTest.callToFunction({ });
    });
});

This way, I'm calling the methods registered inside filter, checking their results. It may not give the biggest testing value but at least I'm reaching 100% coverage in these methods.