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

如何在Jest中Mock使用new关键字实例化的外部服务

解决Jest中Mock使用new关键字实例化模块的问题

我来帮你搞定这个Jest Mock的问题——当模块是用new关键字实例化的时候,确实需要特殊处理一下Mock的方式,不然很容易踩坑。先把你的业务代码补全方便讲解,然后一步步来实现正确的Mock:

首先补全后的myService.js代码(基于你提供的片段完善):

// myService.js
import crypto from 'crypto';
import mayfly from 'mayfly'; // 假设你是通过ES模块导入的

export const checkMayFlyRecord = record => {
  return new Promise((resolve, reject) => {
    const recId = crypto.createHash('md5').update(record.id + record.url).digest('hex');
    const mayflyClient = new mayfly({
      appName: 'cartrecoverynodeserv',
      lifetime: 24 * 3600, // 存储24小时
      crypto: false
    });
    mayflyClient.read(recId, (err, result) => {
      if (err) {
        return reject(err);
      }
      resolve(result);
    });
  });
};

核心思路

我们需要Mockmayfly模块的构造函数,让它返回一个带有Mockread方法的实例,并且这个read方法能按照我们的预期触发回调(因为它是回调式API,需要手动调用回调来resolve/reject外层的Promise)。

测试文件实现(myService.test.js)

// myService.test.js
import { checkMayFlyRecord } from './myService';
import mayfly from 'mayfly';
import crypto from 'crypto';

// 先Mock整个mayfly模块,Jest会自动替换成Mock函数
jest.mock('mayfly');

describe('checkMayFlyRecord', () => {
  // 每个测试用例前清除Mock调用记录,避免用例间污染
  beforeEach(() => {
    mayfly.mockClear();
  });

  test('成功读取记录时,返回预期结果', async () => {
    // 1. 定义Mock的返回数据
    const mockRecordResult = { id: 'test-rec-123', content: 'mock-content' };
    
    // 2. 让mayfly构造函数返回带有Mock read方法的实例
    // 这里手动让read调用传入的回调,模拟成功场景
    mayfly.mockImplementation(() => ({
      read: jest.fn((recordId, callback) => {
        callback(null, mockRecordResult);
      })
    }));

    // 3. 调用被测函数
    const testInput = { id: '123', url: 'https://example.com/cart' };
    const result = await checkMayFlyRecord(testInput);

    // 4. 断言结果符合预期
    expect(result).toEqual(mockRecordResult);

    // 5. 断言mayfly构造函数是否按预期参数调用
    expect(mayfly).toHaveBeenCalledWith({
      appName: 'cartrecoverynodeserv',
      lifetime: 24 * 3600,
      crypto: false
    });

    // 6. 断言read方法是否传入了正确的recId
    const mockReadMethod = mayfly.mock.results[0].value.read;
    const expectedRecId = crypto.createHash('md5').update(testInput.id + testInput.url).digest('hex');
    expect(mockReadMethod).toHaveBeenCalledWith(expectedRecId, expect.any(Function));
  });

  test('读取记录失败时,抛出预期错误', async () => {
    // 1. 定义Mock的错误对象
    const mockReadError = new Error('数据库连接失败');
    
    // 2. 设置构造函数返回的实例,让read触发错误回调
    mayfly.mockImplementation(() => ({
      read: jest.fn((recordId, callback) => {
        callback(mockReadError);
      })
    }));

    // 3. 调用函数并断言reject的错误
    const testInput = { id: '456', url: 'https://example.com/checkout' };
    await expect(checkMayFlyRecord(testInput)).rejects.toThrow(mockReadError);

    // 额外断言构造函数和read方法的调用次数
    expect(mayfly).toHaveBeenCalledTimes(1);
    const mockReadMethod = mayfly.mock.results[0].value.read;
    expect(mockReadMethod).toHaveBeenCalled();
  });
});

关键要点说明

  • jest.mock('mayfly')会自动将mayfly模块替换为一个Mock构造函数,我们可以通过mockImplementation控制它的返回值。
  • 因为mayflyClient.read是回调式API,我们需要在Mock的read方法里手动调用传入的回调函数,才能触发外层Promise的resolve/reject。
  • 每次测试前用mockClear()清除Mock的调用记录,确保每个测试用例都是独立的,不会互相干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 11:11:50