You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Jest中测试函数式Express服务时模拟内部函数?

函数式TypeScript应用中Mock独立函数测试findMovieById的解决方案

问题背景

你在函数式风格的TypeScript Express应用中,通过独立导出的getData函数获取数据,findMovieById依赖该函数实现查询逻辑,但尝试用jest.spyOn和jest-mock-extendedMockgetData时失败,需要正确的Mock方式来测试findMovieById。

核心原因

getData是独立的模块导出函数,而非类方法,所以不能直接用针对类的Mock方式,必须通过Jest的模块拦截机制来Mock它。

具体解决方案

方案1:用jest.mock全局Mock整个模块

假设getData在./data-handlers.ts文件,findMovieById在./movie-service.ts文件,测试时直接Mock整个data-handlers模块:

// movie-service.test.ts
import { findMovieById } from './movie-service';
import * as dataHandlers from './data-handlers';

// Mock整个模块,指定getData的返回值
jest.mock('./data-handlers', () => ({
  getData: jest.fn().mockResolvedValue([
    { id: 1, imdbId: 'tt001', title: '星际穿越' },
    { id: 2, imdbId: 'tt002', title: '盗梦空间' }
  ])
}));

describe('findMovieById', () => {
  test('通过id找到对应电影', async () => {
    const result = await findMovieById('1');
    expect(result).toEqual({ id: 1, imdbId: 'tt001', title: '星际穿越' });
    expect(dataHandlers.getData).toHaveBeenCalledTimes(1);
  });

  test('通过imdbId找到对应电影', async () => {
    const result = await findMovieById('tt002');
    expect(result).toEqual({ id: 2, imdbId: 'tt002', title: '盗梦空间' });
  });

  test('找不到匹配id时返回undefined', async () => {
    const result = await findMovieById('999');
    expect(result).toBeUndefined();
  });
});

方案2:用jest.spyOn动态Mock(支持多测试用例不同返回值)

如果需要在不同测试用例中让getData返回不同数据,用spyOn结合mockResolvedValue更灵活:

// movie-service.test.ts
import { findMovieById } from './movie-service';
import * as dataHandlers from './data-handlers';

describe('findMovieById', () => {
  let getDataSpy: jest.SpyInstance;

  // 每个测试前创建Spy,重置状态
  beforeEach(() => {
    getDataSpy = jest.spyOn(dataHandlers, 'getData');
  });

  // 每个测试后恢复原函数,避免影响其他测试
  afterEach(() => {
    getDataSpy.mockRestore();
  });

  test('返回匹配的电影数据', async () => {
    getDataSpy.mockResolvedValue([{ id: 3, imdbId: 'tt003', title: '蝙蝠侠:黑暗骑士' }]);
    const result = await findMovieById('3');
    expect(result).toEqual({ id: 3, imdbId: 'tt003', title: '蝙蝠侠:黑暗骑士' });
  });

  test('getData返回空数组时返回undefined', async () => {
    getDataSpy.mockResolvedValue([]);
    const result = await findMovieById('1');
    expect(result).toBeUndefined();
  });
});

方案3:依赖注入(更符合函数式编程思想)

修改findMovieById,让它接受getData作为可选参数,测试时直接传入Mock函数,无需依赖Jest的模块Mock:

// 修改后的movie-service.ts
import { getData } from './data-handlers';

export const findMovieById = async (movieId: string, getDataFn = getData) => {
  const movies: any[] = await getDataFn();
  return movies.find(movie => movie.imdbId === movieId || movie.id.toString() === movieId);
}

// 测试文件
import { findMovieById } from './movie-service';

describe('findMovieById', () => {
  test('通过依赖注入Mock getData', async () => {
    const mockGetData = jest.fn().mockResolvedValue([{ id: 4, imdbId: 'tt004', title: '阿甘正传' }]);
    const result = await findMovieById('4', mockGetData);
    expect(result).toEqual({ id: 4, imdbId: 'tt004', title: '阿甘正传' });
    expect(mockGetData).toHaveBeenCalled();
  });
});

常见错误排查

  • 确保Mock的是被测试模块实际导入的函数路径:如果findMovieById中导入的是../utils/data-handlers,测试时就要Mock这个路径,不能Mock相对当前测试文件的路径。
  • 异步函数必须用mockResolvedValue:getData是async函数,返回Promise,不能用mockReturnValue,否则会导致测试等待超时。
  • 不要尝试Mock非导出的函数:getData必须是模块导出的公开函数,否则Jest无法拦截。

内容的提问来源于stack exchange,提问作者user7847018

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 21:55:18