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

如何用Jest同时模拟包含构造函数和函数的模块?

问题分析与解决方案

你的核心问题有两个:

  • 模块顶层的smCli实例创建时机,需确保Jest的mock完全生效后再初始化(不过jest.mock会被自动提升到模块最顶部,所以主要问题是mock的login方法未返回Promise,以及未正确捕获实例的mock方法)
  • 你给login方法返回的是普通值2,但原代码中用await调用它,所以login必须返回Promise,否则会报错或导致测试逻辑不生效

修正后的测试代码

import * as sm from 'some-client';
import { getKey } from './your-module-file'; // 替换成你的实际模块路径

// 自动模拟整个some-client模块
jest.mock('some-client');

// 类型断言,让TypeScript识别mock类型
const MockClient = sm.Client as jest.Mock;
const mockGetCreds = sm.getCreds as jest.Mock<Promise<unknown>>;

describe('getKey', () => {
  let mockLogin: jest.Mock;

  beforeEach(() => {
    // 重置所有mock的调用记录和返回值
    MockClient.mockClear();
    mockGetCreds.mockClear();

    // 定义login方法的mock,返回resolved Promise
    mockLogin = jest.fn().mockResolvedValue(undefined);
    // 让Client类的实例返回包含mockLogin的对象
    MockClient.mockImplementation(() => ({
      login: mockLogin
    }));

    // 模拟getCreds的返回值
    mockGetCreds.mockResolvedValue({ username: 'test', password: 'test' });
  });

  it('should call getCreds and login with correct credentials', async () => {
    await getKey();

    // 断言getCreds被调用了一次
    expect(mockGetCreds).toHaveBeenCalledTimes(1);
    // 断言Client被实例化了一次
    expect(MockClient).toHaveBeenCalledTimes(1);
    // 断言login被调用,参数是getCreds返回的凭据
    expect(mockLogin).toHaveBeenCalledWith({ username: 'test', password: 'test' });
  });
});

关键修正点

  • 给login方法的mock返回mockResolvedValue,确保它是一个Promise,符合原代码中await的调用逻辑
  • 在beforeEach里重置mock,避免不同测试用例之间的污染
  • 单独提取mockLogin变量,方便后续断言它的调用情况

另外,如果你不想用beforeEach,也可以直接在模块级别设置mock,但重置mock是好习惯,能保证测试的独立性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 06:10:59