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

如何在Jest的每个it()测试用例中为jest.mock配置不同的模拟实现

如何在Jest的每个it()测试用例中为jest.mock配置不同的模拟实现

我明白你遇到的问题了——jest.mock的执行时机很特殊,它是在代码编译阶段就运行的,不是测试运行时,所以你把它放在it()里面根本不会生效,反而会被自动提升到文件顶部,导致所有测试用例都共用同一个mock。不过别担心,有个简单可靠的方法可以实现每个测试用例使用不同的mock实现,我给你一步步拆解:

第一步:在文件顶部做基础Mock

先在测试文件最顶部,用jest.mock把你要模拟的中间件模块导出的函数替换成空的jest.fn()。这样这些函数就变成了可动态修改的mock函数,后续每个测试用例都能自定义它们的实现:

// 先在文件顶部完成基础mock,将模块导出的函数替换为可修改的jest mock函数
jest.mock('../../../middleware/awsTransferMiddleware', () => ({
  transferS3Files: jest.fn(),
  filterPassedImage: jest.fn(),
}));

// 导入这个mock后的模块,方便后续在测试用例中修改实现
const { transferS3Files, filterPassedImage } = require('../../../middleware/awsTransferMiddleware');
const request = require('supertest');
const app = require('../../../path/to/your/app');

第二步:在每个测试用例中自定义Mock实现

在每个it()测试用例里,我们先清理mock的历史调用记录和旧实现,再给当前测试设置专属的mock逻辑。这样每个测试用例的mock都是独立的,不会互相影响:

第一个测试用例(模拟正常流程)

it("Should return a 200 status code if at least one image exists in the temp directory.", async () => {
  // 清理mock的历史调用记录和旧实现,避免之前的测试影响当前用例
  transferS3Files.mockClear();
  filterPassedImage.mockClear();

  // 给当前测试设置专属的mock实现
  transferS3Files.mockImplementation(async (req, res, next) => {
    // 模拟中间件正常执行,直接调用next进入下一个流程
    next();
  });

  filterPassedImage.mockImplementation(async (req, res, next) => {
    // 模拟检测到有通过的图片,给req添加测试需要的属性
    req.passedImages = ['test-image.jpg'];
    next();
  });

  // 执行测试请求并断言结果
  const response = await request(app).get('/your-target-endpoint');
  expect(response.statusCode).toBe(200);
  // 还可以验证mock函数是否被正确调用
  expect(transferS3Files).toHaveBeenCalled();
});

第二个测试用例(模拟异常/特殊场景)

it("Should test another scenario with a different mock for the middleware.", async () => {
  // 用mockReset比mockClear更彻底,会清空所有调用记录和之前的实现
  transferS3Files.mockReset();
  filterPassedImage.mockReset();

  // 设置完全不同的mock实现,比如模拟中间件出错的情况
  transferS3Files.mockImplementation(async (req, res, next) => {
    // 模拟S3传输失败,抛出错误给错误处理中间件
    next(new Error('S3 transfer failed'));
  });

  filterPassedImage.mockImplementation(async (req, res, next) => {
    // 模拟没有检测到通过的图片
    req.passedImages = [];
    next();
  });

  // 执行测试并断言异常场景的结果
  const response = await request(app).get('/your-target-endpoint');
  expect(response.statusCode).toBe(500); // 对应错误处理的状态码
  expect(transferS3Files).toHaveBeenCalledTimes(1);
});

补充:如果是默认导出的中间件怎么办?

如果你的中间件是默认导出的单个函数,写法稍微调整一下:

// 顶部mock默认导出的函数
jest.mock('../../../middleware/someDefaultMiddleware', () => jest.fn());

// 导入默认导出的mock函数
const someDefaultMiddleware = require('../../../middleware/someDefaultMiddleware');

// 在测试用例中修改实现
it("Tests default middleware with custom mock", async () => {
  someDefaultMiddleware.mockClear();
  someDefaultMiddleware.mockImplementation((req, res, next) => {
    // 自定义逻辑
    req.customData = 'test';
    next();
  });

  // 后续测试逻辑...
});

这个方法的核心是:先通过顶部的jest.mock把模块替换成可修改的mock函数,然后在运行时(每个测试用例里)动态修改它们的实现,同时用mockClear/mockReset保证测试隔离性,完美解决你需要每个测试用例用不同mock的需求。

备注:内容来源于stack exchange,提问作者Khaled Ramadan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 08:48:05