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

如何在Jest(Node.js)中模拟模块依赖类并验证其方法调用?

问题分析与解决思路

首先先指出你代码里的几个关键错误:

  1. services/s3.js中未创建AWS.S3实例,s3变量未定义,实际运行会直接报错
  2. 测试代码里Bucket: 'BucketName,缺少闭合单引号,语法错误
  3. 测试断言的是deleteObject方法被调用,但uploadAsset实际调用的是upload方法,逻辑不匹配
  4. 测试中创建的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:30:42