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

Jest spyOn.mockImplementation调用原方法 如何按测试用例自定义mock

问题原因

直接使用jest.spyOn无法替换中间件实现的核心原因是:

  • 测试文件导入app/路由实例时,路由初始化阶段就已经拿到了verifyUser的原始函数引用
  • 后续spyOn只是修改了中间件模块的导出属性指向,不会更新路由已经持有的原始函数引用,所以请求进入时还是执行真实逻辑
  • 顶层的jest.mock会被Jest自动提升到所有导入逻辑之前执行,所有引入该中间件的代码拿到的都是mock后的函数引用,所以可以正常生效,但写死实现就没法给不同用例配置不同逻辑。
可落地的实现方案

方案1:顶层空mock + 单测内动态配置实现(推荐)

这个方案适配绝大多数场景,配置简单,用例之间隔离性好:

  1. 首先在测试文件顶部声明模块mock,不要写死实现,只生成空的mock函数,Jest会自动把这段逻辑提升到所有import语句之前执行:
// jest.mock 写在import区域即可,不需要手动挪到最顶部,Jest会自动提升执行优先级
jest.mock('../../middlewares/auth.middleware');

import * as authMiddleware from '../../middlewares/auth.middleware';
import request from 'supertest';
// 剩余其他导入:app实例、其他业务依赖等
  1. 新增统一的重置逻辑,避免不同用例的mock实现、调用记录互相污染:
beforeEach(() => {
  // 每个用例执行前清空所有mock的状态
  jest.resetAllMocks();
});
  1. 在不同测试用例中,按需给verifyUser编写对应的mock逻辑即可:
describe('POST /api/users', () => {
  test('用户仅在外部鉴权系统存在时返回200', async () => {
    // 当前用例的mock逻辑:给req挂载合法用户信息,放行请求
    (authMiddleware.verifyUser as jest.Mock).mockImplementation(
      async (req, _res, next) => {
        req.user = {
          id: '1',
          email: 'user1@email.com',
          fullName: null,
        };
        next();
      }
    );

    const res = await request(app)
      .post('/api/users')
      .send({ fullName: 'Jimmy Neutron' });

    expect(res.status).toEqual(200);
  });

  test('鉴权失败时返回401', async () => {
    // 另一个用例的mock逻辑:直接返回401未授权
    (authMiddleware.verifyUser as jest.Mock).mockImplementation(
      async (_req, res, _next) => {
        res.status(401).json({ error: 'You are not authorized to make this request' });
      }
    );

    const res = await request(app).post('/api/users').send({});
    expect(res.status).toEqual(401);
  });
});

方案2:需要保留真实中间件逻辑的场景

如果部分用例需要走真实的verifyUser逻辑,部分需要mock,可以在顶层mock时引入原始模块实现,默认走真实逻辑,需要mock时再覆盖:

// 顶部mock写法,默认保留真实实现
jest.mock('../../middlewares/auth.middleware', () => {
  const actualModule = jest.requireActual('../../middlewares/auth.middleware');
  return {
    ...actualModule,
    // 给真实函数套一层jest.fn,既可以默认走原逻辑,也支持后续mockImplementation覆盖
    verifyUser: jest.fn(actualModule.verifyUser)
  };
});

这种写法下,默认所有用例都会执行真实的鉴权逻辑,需要mock的用例里直接调用mockImplementation覆盖即可,需要切回真实逻辑时调用(authMiddleware.verifyUser as jest.Mock).mockRestore()就行。

注意点
  • 不要在用例内部单独调用spy.mockRestore()之后直接复用同一个spy实例,容易导致后续用例拿到原始引用不生效,统一用beforeEach做重置更稳妥
  • 如果使用TypeScript,记得给导入的中间件方法加jest.Mock类型断言,避免TS类型报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 06:21:59