如何在Jest单元测试中测试inquirer.prompt的validate与filter函数
我在单元测试中尝试mock inquirer.prompt方法,该方法包含多个问题(示例仅展示两个),代码如下:
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), }, ... ]);
我需要达到指定单元测试覆盖率,因此希望检查filter和validate函数是否被正确调用。现有方案建议将这些函数提取为独立方法后直接测试,但这无法确认filter和validate代码行是否被调用,还会降低代码简洁性。另外,我不想直接调用私有方法,更倾向于调用类的主方法。请问是否可以mock prompt并注入问题答案,同时验证filter和validate部分是否已执行?
完全可以通过mock inquirer.prompt来实现你的需求,不需要拆分函数或者调用私有方法。核心思路是拦截prompt方法,拿到传入的问题配置数组,手动触发其中的filter和validate函数,同时注入模拟的用户输入,最后验证这些函数的调用情况。
以Jest为例,具体步骤如下:
Mock inquirer模块
在测试文件中先mock整个inquirer模块,让prompt方法处于我们的控制之下:jest.mock('inquirer'); import inquirer from 'inquirer'; import YourClass from './your-class-path';拦截prompt调用并验证函数执行
在测试用例中,重写prompt的mock实现,捕获传入的问题列表,针对每个问题的filter和validate做验证:test('验证filter和validate函数被正确触发', async () => { // 模拟用户输入的答案 const mockAnswers = { name1: ' test input ', name2: 'valid input' }; const yourClassInstance = new YourClass(); // 重写prompt的mock逻辑 (inquirer.prompt as jest.Mock).mockImplementation(async (questions) => { // 遍历问题列表,逐个验证filter和validate questions.forEach(question => { const inputValue = mockAnswers[question.name]; if (question.filter) { // 执行filter并验证结果 const filtered = question.filter(inputValue); expect(filtered).toBe(inputValue.trim()); // 验证name1的trim处理生效 } if (question.validate) { // 执行validate并验证返回值 const validateResult = question.validate(inputValue); expect(validateResult).toBe(true); // 假设验证通过时返回true } }); return mockAnswers; }); // 调用类的主方法,触发整个流程 await yourClassInstance.mainMethod(); // 确认prompt确实被调用过 expect(inquirer.prompt).toHaveBeenCalled(); });验证异常场景
如果要测试validate在输入不合法时的行为,只需传入无效模拟输入,断言验证结果符合预期:test('验证validate在输入为空时返回错误信息', async () => { const invalidAnswers = { name2: '' }; const yourClassInstance = new YourClass(); (inquirer.prompt as jest.Mock).mockImplementation(async (questions) => { const name2Question = questions.find(q => q.name === 'name2'); if (name2Question?.validate) { const validateResult = name2Question.validate(invalidAnswers.name2); expect(validateResult).toBe('输入不能为空'); // 匹配你的错误提示 } return invalidAnswers; }); await yourClassInstance.mainMethod(); });
这种方式既保留了原代码的简洁性,又不需要调用私有方法,完全通过触发类的主方法来覆盖流程,同时能精准验证filter和validate的执行情况,满足覆盖率要求。
内容的提问来源于stack exchange,提问作者mardo

