如何在Jest(Node.js)中模拟模块依赖类并验证其方法调用?
问题分析与解决思路
首先先指出你代码里的几个关键错误:
services/s3.js中未创建AWS.S3实例,s3变量未定义,实际运行会直接报错- 测试代码里
Bucket: 'BucketName,缺少闭合单引号,语法错误 - 测试断言的是
deleteObject方法被调用,但uploadAsset实际调用的是upload方法,逻辑不匹配 - 测试中创建的
s3实例和uploadAsset内部的实例不是同一个,导致spy无法捕获调用
下面给出两种可行的解决方案:
方案一:通过监视S3类的原型方法
这种方式不需要修改业务代码的导出,直接通过Jest模拟模块时暴露mock函数,实现对所有实例方法的监视。
修正后的services/s3.js
const AWS = require('aws-sdk'); // 补上缺失的S3实例创建 const s3 = new AWS.S3(); const uploadAsset = async (param) => { try { const response = await s3.upload(param).promise(); return response; } catch (e) { console.log(e); } } module.exports = { uploadAsset }
测试代码services.s3.test.js
const AWS = require('aws-sdk'); const { uploadAsset } = require('../services/s3') jest.mock('aws-sdk', () => { // 提前定义可监视的mock函数 const mockUpload = jest.fn().mockReturnValue({ promise: () => Promise.resolve({ Location: 'http://test-bucket.s3.amazonaws.com/test-key', Key: 'test-key' }) }); return { S3: class { constructor() {} // 将mock函数绑定到类的upload方法 upload: mockUpload } } }); describe('uploadAsset() functionality', () => { it('should call S3 upload method with correct params', async () => { const base64Str = '/9j/4AAQSkZJRgABAQAAAQABAAD/'; const buffer = Buffer.from(base64Str, 'base64'); const testParams = { Bucket: 'BucketName', Key: 'KeyName', Body: buffer, }; // 获取mock的upload方法 const uploadSpy = AWS.S3.prototype.upload; await uploadAsset(testParams); // 断言upload方法被调用,且参数匹配 expect(uploadSpy).toHaveBeenCalledTimes(1); expect(uploadSpy).toHaveBeenCalledWith(testParams); }); });
方案二:导出S3实例并直接监视
如果允许修改业务代码的导出,可以直接导出内部的S3实例,在测试中直接监视该实例的方法。
修正后的services/s3.js
const AWS = require('aws-sdk'); const s3 = new AWS.S3(); const uploadAsset = async (param) => { try { const response = await s3.upload(param).promise(); return response; } catch (e) { console.log(e); } } // 导出s3实例供测试使用 module.exports = { uploadAsset, s3 };
测试代码services.s3.test.js
const { uploadAsset, s3 } = require('../services/s3') jest.mock('aws-sdk', () => { return { S3: class { constructor() {} upload(param) { return { promise: () => Promise.resolve({ Location: `http://${param.Bucket}.s3.amazonaws.com/${param.Key}`, Key: param.Key }) } } } } }); describe('uploadAsset() functionality', () => { it('should upload asset and call S3 upload', async () => { const base64Str = '/9j/4AAQSkZJRgABAQAAAQABAAD/'; const buffer = Buffer.from(base64Str, 'base64'); const testParams = { Bucket: 'BucketName', Key: 'KeyName', Body: buffer, }; // 在调用业务方法前创建spy const uploadSpy = jest.spyOn(s3, 'upload').mockImplementation(param => ({ promise: () => Promise.resolve({ Location: `http://${param.Bucket}.s3.amazonaws.com/${param.Key}`, Key: param.Key }) })); const response = await uploadAsset(testParams); expect(uploadSpy).toHaveBeenCalledWith(testParams); expect(response.Key).toBe(testParams.Key); // 清理spy,避免影响其他测试 uploadSpy.mockRestore(); }); });
内容的提问来源于stack exchange,提问作者Mike Guijarro
相关产品推荐
相关产品推荐

