我正在开发一个简单的AWS Lambda函数,它由DynamoDB Streams事件触发,并应将除“REMOVE”事件外的所有记录转发到SQS队列。该函数按预期工作,没有任何意外。
我想编写一个单元测试来测试不提交任何内容到SQS时的行为,当它是一个“DELETE”事件时。我首先尝试使用“aws-sdk-mock”来实现这一点。正如您在函数代码中所看到的那样,我尝试遵循Lambda最佳实践,通过在处理程序代码之外初始化SQS客户端。显然,这会防止“aws-sdk-mock”能够模拟SQS服务(GitHub上有一个问题:https://github.com/dwyl/aws-sdk-mock/issues/206)。
然后我尝试使用“jest”来模拟SQS,这需要更多的代码才能做到正确,但我最终遇到了同样的问题,被要求将SQS的初始化放在处理程序函数内部,这违反了Lambda最佳实践。
我该如何编写一个单元测试来测试这个函数,同时让SQS客户端的初始化(
我想编写一个单元测试来测试不提交任何内容到SQS时的行为,当它是一个“DELETE”事件时。我首先尝试使用“aws-sdk-mock”来实现这一点。正如您在函数代码中所看到的那样,我尝试遵循Lambda最佳实践,通过在处理程序代码之外初始化SQS客户端。显然,这会防止“aws-sdk-mock”能够模拟SQS服务(GitHub上有一个问题:https://github.com/dwyl/aws-sdk-mock/issues/206)。
然后我尝试使用“jest”来模拟SQS,这需要更多的代码才能做到正确,但我最终遇到了同样的问题,被要求将SQS的初始化放在处理程序函数内部,这违反了Lambda最佳实践。
我该如何编写一个单元测试来测试这个函数,同时让SQS客户端的初始化(
const sqs: SQS = new SQS()
)在处理程序之外进行?我是错误地模拟了服务还是处理程序的结构必须改变才能更轻松地进行测试?我知道这个Lambda函数非常简单,可能不需要进行单元测试,但我将不得不编写更复杂逻辑的其他Lambda函数,并且我认为这个函数非常适合演示问题。
index.ts
import {DynamoDBStreamEvent, DynamoDBStreamHandler} from "aws-lambda";
import SQS = require("aws-sdk/clients/sqs");
import DynamoDB = require("aws-sdk/clients/dynamodb");
const sqs: SQS = new SQS()
export const handleDynamoDbEvent: DynamoDBStreamHandler = async (event: DynamoDBStreamEvent, context, callback) => {
const QUEUE_URL = process.env.TARGET_QUEUE_URL
if (QUEUE_URL.length == 0) {
throw new Error('TARGET_QUEUE_URL not set or empty')
}
await Promise.all(
event.Records
.filter(_ => _.eventName !== "REMOVE")
.map((record) => {
const unmarshalled = DynamoDB.Converter.unmarshall(record.dynamodb.NewImage);
let request: SQS.SendMessageRequest = {
MessageAttributes: {
"EVENT_NAME": {
DataType: "String",
StringValue: record.eventName
}
},
MessageBody: JSON.stringify(unmarshalled),
QueueUrl: QUEUE_URL,
}
return sqs.sendMessage(request).promise()
})
);
}
index.spec.ts
import {DynamoDBRecord, DynamoDBStreamEvent, StreamRecord} from "aws-lambda";
import {AttributeValue} from "aws-lambda/trigger/dynamodb-stream";
import {handleDynamoDbEvent} from "./index";
import {AWSError} from "aws-sdk/lib/error";
import {PromiseResult, Request} from "aws-sdk/lib/request";
import * as SQS from "aws-sdk/clients/sqs";
import {mocked} from "ts-jest/utils";
import DynamoDB = require("aws-sdk/clients/dynamodb");
jest.mock('aws-sdk/clients/sqs', () => {
return jest.fn().mockImplementation(() => {
return {
sendMessage: (params: SQS.Types.SendMessageRequest, callback?: (err: AWSError, data: SQS.Types.SendMessageResult) => void): Request<SQS.Types.SendMessageResult, AWSError> => {
// @ts-ignore
const Mock = jest.fn<Request<SQS.Types.SendMessageResult, AWSError>>(()=>{
return {
promise: (): Promise<PromiseResult<SQS.Types.SendMessageResult, AWSError>> => {
return new Promise<PromiseResult<SQS.SendMessageResult, AWSError>>(resolve => {
resolve(null)
})
}
}
})
return new Mock()
}
}
})
});
describe.only('Handler test', () => {
const mockedSqs = mocked(SQS, true)
process.env.TARGET_QUEUE_URL = 'test'
const OLD_ENV = process.env;
beforeEach(() => {
mockedSqs.mockClear()
jest.resetModules();
process.env = {...OLD_ENV};
});
it('should write INSERT events to SQS', async () => {
console.log('Starting test')
await handleDynamoDbEvent(createEvent(), null, null)
expect(mockedSqs).toHaveBeenCalledTimes(1)
});
})
new SQSQueueClient(QUEUE_URL)
,这将导致构造函数中的new SQS()
被调用。这与Lambda最佳实践不符。 - mheckSQSQueueClient
的创建可以移至外部,队列 URL 可能需要作为参数传递给send()
。但是,想法可能仍然相同。 - Phuong NguyenSQSQueueClient
的创建移动到外部。从环境变量解析队列URL的逻辑也可以移到SQSQueueClient
的构造函数中。 - Phuong Nguyen