Jest(Node.js)测试AWS SES.promise()方法报错,求解决方案
解决Node.js AWS SES模板邮件函数的Jest测试问题
你的测试遇到的核心问题是没有正确模拟AWS SES客户端的sendTemplatedEmail方法返回值结构——原代码里调用了.promise()链式方法,所以你的mock必须返回一个包含promise()方法的对象,而不是直接处理callback。另外异步错误的测试逻辑也需要调整,因为async函数的错误会以Promise rejection的形式抛出,不能用同步的expect(() => ...).toThrow()。
下面是完整的修复方案和测试代码:
1. 基础Jest Mock方案(不依赖aws-sdk-mock)
首先,我们需要正确mock AWS SDK的SES类及其方法,确保sendTemplatedEmail返回的对象带有promise()方法:
完整测试代码(function.test.js)
const aws = require('aws-sdk'); const index = require('./function'); // 提前mock AWS SES类 jest.mock('aws-sdk', () => { const mockSendTemplatedEmail = jest.fn().mockReturnThis(); const mockPromise = jest.fn(); return { SES: jest.fn(() => ({ sendTemplatedEmail: mockSendTemplatedEmail, promise: mockPromise })) }; }); describe('sesSendEmail', () => { beforeEach(() => { // 重置所有mock的调用记录 jest.clearAllMocks(); // 重置console.log的spy jest.spyOn(console, 'log').mockImplementation(() => {}); }); test('should send templated email successfully', async () => { const mockResponse = { MessageId: 'mock-message-id-123' }; // 让promise方法resolve为mock响应 aws.SES().promise.mockResolvedValue(mockResponse); const testMessage = JSON.stringify({ user: 'test-user' }); await index.sesSendEmail(testMessage); // 断言SES客户端初始化正确 expect(aws.SES).toHaveBeenCalledWith({ apiVersion: '2020-12-01' }); // 断言sendTemplatedEmail的参数正确 expect(aws.SES().sendTemplatedEmail).toHaveBeenCalledWith({ Source: 'abc@gmail.com', Template: 'deviceUsageStatisticsEmailTemplate', Destination: { ToAddresses: ['xyz@gmail.com'] }, TemplateData: testMessage, }); // 断言console.log打印了响应 expect(console.log).toHaveBeenCalledWith(mockResponse); }); test('should handle error when sending email fails', async () => { const mockError = new Error('Network error'); // 让promise方法reject为mock错误 aws.SES().promise.mockRejectedValue(mockError); const testMessage = 'mock message'; await index.sesSendEmail(testMessage); // 断言console.log打印了错误 expect(console.log).toHaveBeenCalledWith(mockError); // 注意:原函数内部catch了错误,所以不会抛出到外部,这里不需要断言toThrow }); });
关键修复点说明:
- 使用
jest.mock('aws-sdk')直接mock整个SDK,确保SES构造函数返回的对象包含sendTemplatedEmail和promise方法 sendTemplatedEmail用mockReturnThis()实现链式调用(因为原代码是ses.sendTemplatedEmail(params).promise())- 异步错误测试中,原函数内部已经
catch了错误并打印,所以不会向外抛出异常,我们只需要断言console.log捕获了错误即可
2. 使用aws-sdk-mock的正确姿势
如果你偏好使用aws-sdk-mock,需要注意以下几点:
- 确保在导入目标函数之前调用
aws.mock() - 正确指定服务名(首字母大写的
SES)和方法名(sendTemplatedEmail) - 模拟方法需要返回符合Promise格式的结果
示例代码:
const AWSMock = require('aws-sdk-mock'); const aws = require('aws-sdk'); const index = require('./function'); describe('sesSendEmail with aws-sdk-mock', () => { beforeEach(() => { AWSMock.reset(); jest.spyOn(console, 'log').mockImplementation(() => {}); }); test('should send templated email successfully', async () => { const mockResponse = { MessageId: 'mock-id' }; // 正确mock SES的sendTemplatedEmail方法 AWSMock.mock('SES', 'sendTemplatedEmail', (params, callback) => { callback(null, mockResponse); }); await index.sesSendEmail('test-message'); expect(console.log).toHaveBeenCalledWith(mockResponse); AWSMock.restore('SES'); }); test('should handle error', async () => { const mockError = new Error('Network issue'); AWSMock.mock('SES', 'sendTemplatedEmail', (params, callback) => { callback(mockError); }); await index.sesSendEmail('test-message'); expect(console.log).toHaveBeenCalledWith(mockError); AWSMock.restore('SES'); }); });
常见错误排查:
- 报错
Cannot stub non-existent own property ses:是因为你用了小写的ses而不是正确的服务名SES(aws-sdk-mock要求服务名首字母大写) - 确保
aws-sdk-mock的版本与aws-sdk兼容,建议使用最新稳定版
内容的提问来源于stack exchange,提问作者Ali Khan
相关产品推荐
相关产品推荐

