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

在Express应用中使用Jest模拟ES6模块函数遇错求助

解决ES6模块下Jest模拟createUser函数的问题

问题原因

ES模块的导出是只读绑定,直接用jest.spyOn尝试修改模块导出的函数会触发TypeError: Cannot assign to read only property 'createUser' of object '[object Module]',导致模拟失效,实际函数仍会执行。

解决方案

改用jest.mock模拟整个模块,同时保留其他未需要模拟的函数的实际实现,再在不同测试用例中动态修改createUser的mock行为。

步骤1:在测试文件顶部模拟userServices模块

确保在导入app之前执行mock,避免app加载真实的userServices模块:

import * as userServices from './userServices';
import request from 'supertest';
import app from './app';

// 模拟userServices模块,仅替换createUser为mock函数,其他函数保留实际实现
jest.mock('./userServices', () => ({
  ...jest.requireActual('./userServices'),
  createUser: jest.fn(),
}));

步骤2:在测试用例中设置mock行为

测试注册接口处理500错误的场景

用mockRejectedValue模拟异步函数抛出错误(更贴合异步函数的错误场景):

describe('Test API', () => {
  const request = supertest.agent(app);

  test('handles internal server error (500)', async () => {
    // 模拟createUser抛出错误
    userServices.createUser.mockRejectedValue(new Error('Simulated internal server error'));

    const payload = {
      email: 'test@example.com',
      password: 'test123'
      // 其他必要字段
    };

    const response = await request.post('/register').send(payload);

    // 断言
    expect(response.status).toBe(500);
    expect(userServices.createUser).toHaveBeenCalledWith(payload);
    expect(response.body).toHaveProperty('message', 'Internal server error');
  });
});

不同测试用例设置不同mock行为

如果需要在另一个测试中模拟注册成功的场景,只需重新设置mock的返回值:

test('successfully registers user (201)', async () => {
  const mockUser = {
    id: 1,
    email: 'test@example.com',
    requestCount: 0
  };

  // 模拟createUser成功返回用户
  userServices.createUser.mockResolvedValue(mockUser);

  const payload = {
    email: 'test@example.com',
    password: 'test123'
  };

  const response = await request.post('/register').send(payload);

  expect(response.status).toBe(201);
  expect(userServices.createUser).toHaveBeenCalledWith(payload);
  expect(response.body).toEqual(mockUser);
});

关键说明

  • jest.requireActual('./userServices'):保留模块中其他未需要模拟的函数的真实实现,避免影响其他测试逻辑。
  • mockRejectedValue/mockResolvedValue:专门用于模拟异步函数的拒绝/成功状态,比mockImplementation更简洁直观。
  • 测试文件的导入顺序:必须先执行jest.mock,再导入app,确保app使用的是mock后的userServices模块。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 14:45:24