如何在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
相关产品推荐
相关产品推荐

