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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 12:33:17