Jest中Mock AWS SDK的SQS.receiveMessage方法失效问题排查
问题:Jest模拟AWS SQS receiveMessage方法仅局部生效
业务代码:sqsHelper.js
const AWS = require("aws-sdk"); export default class SqsHelper { static SqsGetMessagesTest = () => { const sqs = new AWS.SQS({ apiVersion: serviceConfig.sqs.api_version, region: serviceConfig.sqs.region, }); const queueURL = "https://sqs.us-west-2.amazonaws.com/<1234>/<4567>"; const params = { AttributeNames: ["SentTimestamp"], MaxNumberOfMessages: 10, MessageAttributeNames: ["All"], QueueUrl: queueURL, VisibilityTimeout: 20, WaitTimeSeconds: 20, }; return new Promise((resolve, reject) => { sqs.receiveMessage(params, async (recErr, recData) => { if (recErr) { reject(recErr); } else if (recData.Messages) { console.info(`Message count: ${recData.Messages.length}`); resolve(recData.Messages); } }); }); }; }
测试代码:sqsHelper.test.js
import SqsHelper from "../../helpers/sqsHelper.js"; import { SQS } from "aws-sdk"; const dumyData = { Messages: [{ name: "123", lastName: "456" }] }; const sqs = new SQS(); describe("Test SQS helper", () => { test("Recieve message", async () => { jest.spyOn(sqs, 'receiveMessage').mockReturnValue(dumyData); // check 1 const res1 = await sqs.receiveMessage(); console.log(`res: ${JSON.stringify(res1, null, 2)}`) expect(res1).toEqual(dumyData); // check 2 const res2 = await SqsHelper.SqsGetMessagesTest(); console.log(`res2: ${JSON.stringify(res2, null, 2)}`); expect(res2).toBe(dumyData); }); });
问题现象
测试中直接调用模拟后的sqs.receiveMessage方法能正常返回假数据,但调用SqsHelper.SqsGetMessagesTest方法时,模拟未生效,触发真实请求并报错:InvalidClientTokenId: The security token included in the request is invalid.
问题原因
- 你仅对测试文件内局部创建的sqs实例做了模拟,但业务代码里是通过
new AWS.SQS()创建了全新的独立实例,这个新实例未被Jest拦截,因此会发起真实API请求。 receiveMessage采用回调式调用,直接返回dummyData不符合它的调用逻辑,无法触发业务代码里的回调处理。
修复后的测试代码
import SqsHelper from "../../helpers/sqsHelper.js"; import AWS from "aws-sdk"; const dummyData = { Messages: [{ name: "123", lastName: "456" }] }; describe("Test SQS helper", () => { let receiveMessageMock; beforeEach(() => { // 模拟AWS.SQS构造函数,让所有new出来的实例都带mock方法 receiveMessageMock = jest.fn((params, callback) => { // 适配业务代码的回调逻辑,传入成功数据 callback(null, dummyData); }); jest.spyOn(AWS, "SQS").mockImplementation(() => ({ receiveMessage: receiveMessageMock })); // 若serviceConfig是外部依赖,需同步模拟(示例) // jest.mock('../../path/to/serviceConfig', () => ({ // sqs: { api_version: '2012-11-05', region: 'us-west-2' } // })); }); afterEach(() => { // 清理mock,避免污染其他测试用例 jest.restoreAllMocks(); }); test("Receive message", async () => { const result = await SqsHelper.SqsGetMessagesTest(); // 注意:业务代码resolve的是recData.Messages,而非整个dummyData expect(result).toEqual(dummyData.Messages); // 验证receiveMessage是否按预期调用 expect(receiveMessageMock).toHaveBeenCalledTimes(1); expect(receiveMessageMock).toHaveBeenCalledWith( expect.objectContaining({ QueueUrl: "https://sqs.us-west-2.amazonaws.com/<1234>/<4567>", MaxNumberOfMessages: 10 }), expect.any(Function) ); }); });
关键改动说明
- 全局拦截SQS实例创建:通过mock
AWS.SQS构造函数,让业务代码里所有new AWS.SQS()生成的实例都带有模拟方法。 - 适配回调调用逻辑:模拟
receiveMessage时主动触发回调函数,匹配业务代码的异步处理逻辑。 - 修正断言逻辑:业务代码最终返回的是
recData.Messages,断言需对应这个值而非完整的dummyData。 - 增加mock清理:用
afterEach恢复所有mock,避免影响其他测试用例。
内容的提问来源于stack exchange,提问作者David Gershtenkoren
相关产品推荐
相关产品推荐

