如何在Jest中正确模拟外部函数调用并返回自定义虚拟结果
Jest 模拟指定函数的实现方案
前置说明
你要模拟的三个函数如果是从其他模块导入的,可直接按下方方案操作;如果是和generateSalamWelcome定义在同一个文件内,需要先调整原函数的调用方式:将三个函数的调用改为从当前文件的导出对象上调用,示例如下:
// templeate.ts 调整示例 import * as currentModule from './templeate'; // 原有三个函数的导出保持不变 export const getRestaurantQueriesMultiple = async () => {/* 原有逻辑 */} export const getMode = () => {/* 原有逻辑 */} export const generateTextTemplate = () => {/* 原有逻辑 */} export const generateSalamWelcome = async function ( actionSource: IActionSource, actionDetails: IActionDetails ): Promise<IReplyTemplate[] | undefined> { try { const { pageId } = actionSource; const { actionExt } = actionDetails; // 调用时加上currentModule前缀 const restaurantData = await currentModule.getRestaurantQueriesMultiple(pageId, []); if (restaurantData) { const replyTemplates: IReplyTemplate[] = []; const { enOnly, bnOnly } = restaurantData; // 调用时加上currentModule前缀 const mode = currentModule.getMode(enOnly, bnOnly, actionExt || languageModes.EN); replyTemplates.push( // 调用时加上currentModule前缀 currentModule.generateTextTemplate( selectTextEnBn(mode, strings.salamWelcomeEn, strings.salamWelcomeBn) ) ); return replyTemplates; } } catch (err) { logger.error(err); } };
完整测试代码示例
假设三个函数统一从./common-utils模块导入,测试文件templeate.test.ts写法如下:
import { generateSalamWelcome } from './templeate'; // 导入三个函数所在的模块 import * as utilsModule from './common-utils'; // 导入用到的类型定义 import type { IActionSource, IActionDetails, IReplyTemplate } from './type-defs'; // 全局模拟整个工具模块 jest.mock('./common-utils'); describe('generateSalamWelcome 函数测试', () => { // 每次测试前清空模拟的调用记录与自定义返回值,避免用例间互相影响 beforeEach(() => { jest.clearAllMocks(); }); it('拿到餐厅数据时返回正确的欢迎模板', async () => { // 1. 自定义getRestaurantQueriesMultiple的返回值 const mockRestaurantData = { enOnly: false, bnOnly: true, // 补全其他restaurantData需要的字段 }; // 异步函数用mockResolvedValue定义返回值 jest.mocked(utilsModule.getRestaurantQueriesMultiple).mockResolvedValue(mockRestaurantData); // 2. 自定义getMode的返回值,比如返回孟加拉语模式 const mockLangMode = 'BN'; // 同步函数用mockReturnValue定义返回值 jest.mocked(utilsModule.getMode).mockReturnValue(mockLangMode); // 3. 自定义generateTextTemplate的返回值 const mockReplyTemplate: IReplyTemplate = { type: 'text', content: 'আসসালামু আলাইকুম! আমাদের রেস্তোরাঁয় স্বাগতম', // 补全其他IReplyTemplate需要的字段 }; jest.mocked(utilsModule.generateTextTemplate).mockReturnValue(mockReplyTemplate); // 构造入参 const mockActionSource = { pageId: 'test_page_001' } as IActionSource; const mockActionDetails = { actionExt: 'BN' } as IActionDetails; // 调用待测函数 const result = await generateSalamWelcome(mockActionSource, mockActionDetails); // 断言结果符合预期 expect(result).toEqual([mockReplyTemplate]); // 可选:断言模拟函数的调用参数符合预期,验证传参逻辑是否正确 expect(utilsModule.getRestaurantQueriesMultiple).toHaveBeenCalledWith('test_page_001', []); expect(utilsModule.getMode).toHaveBeenCalledWith(false, true, 'BN'); }); });
补充说明
如果测试时出现TS类型报错,可以用as jest.Mock手动断言函数类型,更推荐用Jest内置的jest.mocked工具函数,会自动保留函数的类型提示。
内容的提问来源于stack exchange,提问作者Al Imran Hossain
相关产品推荐
相关产品推荐

