如何在Jest中监视嵌套的AWS Lambda.invoke及promise方法
如何正确监视AWS Lambda的.invoke和.promise函数进行测试
测试AWS Lambda函数时,希望验证lambda.invoke方法是否被传入正确参数,但测试始终提示该方法未被调用,怀疑监视方式有误。
待测试函数
/* Amplify Params - DO NOT EDIT ENV FUNCTION_EMAIL_NAME STRIPE_ENDPOINT_SECRET REGION Amplify Params - DO NOT EDIT */ const stripe = require('stripe'); const AWS = require('aws-sdk'); /** * @type {import('@types/aws-lambda').APIGatewayProxyHandler} */ exports.handler = async (event) => { console.log(`EVENT: ${JSON.stringify(event)}`); const lambda = new AWS.Lambda({ region: process.env.REGION }); const sig = event.headers['Stripe-Signature']; let stripeEvent; try { stripeEvent = stripe.webhooks.constructEvent( event.body, sig, process.env.STRIPE_ENDPOINT_SECRET ); } catch (err) { return { statusCode: 400, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*', }, body: JSON.stringify(`Webhook Error: ${err.message}`), }; } // Handle the event switch (stripeEvent.type) { case 'payment_intent.succeeded': // eslint-disable-next-line no-case-declarations const paymentIntent = stripeEvent.data.object; // Then define and call a function to handle the stripeEvent payment_intent.succeeded try { const result = await lambda .invoke({ FunctionName: process.env.FUNCTION_EMAIL_NAME, Payload: JSON.stringify({ ...event, pathParameters: { userID: paymentIntent.metadata.userID, }, body: JSON.stringify({ user: { emailAddress: paymentIntent.metadata.email, }, }), }), }) .promise(); if (result.StatusCode === 200) { return { statusCode: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*', }, body: JSON.stringify('Email successfully sent!'), }; } } catch (err) { console.log('Email failed!', err); } break; // ... handle other stripeEvent types default: console.log(`Unhandled stripeEvent type ${stripeEvent.type}`); } return { statusCode: 200, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': '*', }, body: JSON.stringify('Hello from Lambda!'), }; };
失败的测试代码
const AWS = require('aws-sdk'); const stripe = require('stripe'); const { handler } = require('./index'); const event = require('./event.json'); jest.mock('stripe'); jest.mock('aws-sdk'); describe('handler', () => { const { env } = process; beforeEach(() => { jest.resetAllMocks(); jest.restoreAllMocks(); jest.clearAllMocks(); jest.resetModules(); process.env = { ...env, REGION: 'us-east-1', FUNCTION_EMAIL_NAME: 'example_function_name', STRIPE_ENDPOINT_SECRET: 'stripe_test_key', }; }); afterEach(() => { process.env = env; }); it('should call the email lambda if payment intent succeeded', async () => { const lambdaSpy = jest.spyOn(AWS, 'Lambda'); const stripeSpy = jest.spyOn(stripe.webhooks, 'constructEvent'); const test = jest.fn(); stripeSpy.mockReturnValue({ type: 'succeeded', }); lambdaSpy.mockReturnValue({ invoke: test, promise: jest.fn() }); await handler(event); expect(test).toHaveBeenCalled(); }); });
问题分析与解决方案
问题1:Stripe事件类型不匹配
原代码触发invoke的条件是stripeEvent.type === 'payment_intent.succeeded',但测试中mock的返回值是{ type: 'succeeded' },类型不匹配导致无法进入目标分支,自然不会调用invoke。
问题2:Lambda实例的Mock方式错误
原代码通过new AWS.Lambda()创建实例后,调用invoke方法再链式调用promise()。测试中直接给Lambda构造函数返回{ invoke: test, promise: jest.fn() }的结构错误——promise是invoke返回对象的方法,不是实例的方法。
修正后的测试代码
const AWS = require('aws-sdk'); const stripe = require('stripe'); const { handler } = require('./index'); const event = require('./event.json'); jest.mock('stripe'); jest.mock('aws-sdk'); describe('handler', () => { const { env } = process; beforeEach(() => { jest.resetAllMocks(); jest.clearAllMocks(); process.env = { ...env, REGION: 'us-east-1', FUNCTION_EMAIL_NAME: 'example_function_name', STRIPE_ENDPOINT_SECRET: 'stripe_test_key', }; // 正确mock Stripe事件,匹配目标类型并包含所需metadata stripe.webhooks.constructEvent.mockReturnValue({ type: 'payment_intent.succeeded', data: { object: { metadata: { userID: 'test-user-id', email: 'test@example.com' } } } }); // 模拟invoke方法返回带有promise的对象,promise resolve出正确结果 const mockInvoke = jest.fn().mockReturnValue({ promise: jest.fn().mockResolvedValue({ StatusCode: 200 }) }); // 让Lambda构造函数返回带有invoke方法的实例 AWS.Lambda.mockImplementation(() => ({ invoke: mockInvoke })); }); afterEach(() => { process.env = env; }); it('should call the email lambda if payment intent succeeded', async () => { // 获取mock的invoke方法 const mockInvoke = AWS.Lambda.mock.results[0].value.invoke; await handler(event); // 验证invoke被调用 expect(mockInvoke).toHaveBeenCalled(); // 还可以验证传入的参数是否正确 expect(mockInvoke).toHaveBeenCalledWith(expect.objectContaining({ FunctionName: 'example_function_name', Payload: expect.any(String) })); }); });
关键修正点
- Stripe事件Mock:返回
type: 'payment_intent.succeeded',并补充data.object.metadata结构,确保代码能读取到userID和email。 - Lambda实例Mock:
- 用
mockImplementation让AWS.Lambda构造函数返回包含invoke方法的实例。 invoke方法返回包含promise方法的对象,promise方法mockResolvedValue返回{ StatusCode: 200 },确保代码能进入成功分支。
- 用
- 验证逻辑:通过
AWS.Lambda.mock.results[0].value.invoke获取mock的invoke方法,进行调用验证,还可进一步验证传入参数的细节。
内容的提问来源于stack exchange,提问作者klaurtar1
相关产品推荐
相关产品推荐

