使用伪造的S3Event测试我的AWS Lambda函数

时间:2018-12-07 14:20:34

标签: java amazon-web-services aws-lambda

我正在为从S3触发,获取一些文件数据并放入另一个S3存储桶的AWS Lambda实现Java 8程序包。

这是我当前的Handler

public class Handler implements RequestHandler<S3Event, Void> {
    private static final String TARGET_BUCKET = "some-bucket";

    private AmazonS3Client s3Client = new AmazonS3Client(new DefaultAWSCredentialsProviderChain());
    private Runner runner = new Runner(s3Client, TARGET_BUCKET);

    @Override
    public Void handleRequest(S3Event s3Event, Context context) {
        runner.run(s3Event, context);

        return null;
    }
}

我已将业务逻辑移至我的Runner类,以便可以正确地对其进行测试(紧随AWS Lambda best practices white paper之后)。

但是,我正在努力查看如何传递伪造的S3Event来测试我的run函数。

我目前的测试是:

@Test
public void putsDataIntoS3() throws IOException {
    runner.run(new ObjectMapper().readValue(loadJsonFromFile("s3-event.json"), S3Event.class), context);

    assertTrue(true);
}

loadJsonFromFile在获取带有我传递的文件名的资源时,将其转换为输入流,然后转换为String

但是,这会导致错误:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class com.amazonaws.services.lambda.runtime.events.S3Event]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)

所以我的问题是,如何通过传递伪造的run JSON来正确测试S3Event函数?

这些是我正在使用的与aws相关的依赖项:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-core</artifactId>
    <version>1.1.0</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-events</artifactId>
    <version>2.2.2</version>
</dependency>
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-s3</artifactId>
    <version>1.11.455</version>
</dependency>

编辑:

我可以像这样使用S3Event.parseJson函数: 我还看到了parseJson函数,可以这样使用它:

@Test
public void putsFilteredDataIntoS3() throws IOException {
    runner.run(new S3Event(S3Event.parseJson(loadJsonFromFile("s3-event.json")).getRecords()), context);

    assertTrue(true);
}

但这是最佳做法吗?

3 个答案:

答案 0 :(得分:3)

您可以使用Mockito库并尝试模拟此S3Event

从JSON创建S3Event的另一个选项:

S3EventNotification notification = S3EventNotification.parseJson(loadJsonFromFile("s3-event.json"));
S3Event event = new S3Event(notification.getRecords());

编辑: 第三种选择是将aws-lambda-java-events更新为2.2.4版本,他们在其中添加了S3Event的默认构造函数,因此您可以像这样反序列化它:

objectMapper.readValue(loadJsonFromFile("s3-event.json"), S3Event.class)

答案 1 :(得分:1)

如果您使用的是 AWS SDK 的第 2 版,最简单的方法是使用亚马逊内部使用的反序列化器:

AWS Lambda Java 运行时序列化

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-serialization</artifactId>
    <version>1.0.0</version>
</dependency>

它的工作原理如下:

PojoSerializer<S3Event> s3EventSerializer = LambdaEventSerializers.serializerFor(S3Event.class, ClassLoader.getSystemClassLoader());
S3Event event = s3EventSerializer.fromJson(inputStream);

有一件重要的事情需要了解(AWS SDK 版本 2 与版本 1)

如果您的 JSON 有问题(不兼容的字段值或 JodaTime 库不在类路径中),序列化程序什么也不说并激活 AWS SDK 1 回退。最后你会得到这样的错误:

java.lang.ClassNotFoundException: com.amazonaws.services.s3.event.S3EventNotification$S3EventNotificationRecord.

此消息错误。 AWS 解串器与版本 2 和版本 1 完美配合。库本身不依赖于 SDK(既不依赖于 1 也不依赖于 2)。您很可能需要检查 JSON。

答案 2 :(得分:0)

我一直在S3Event和S3NotificationEvent这两个软件包之间遇到问题。

由于以下原因,我无法将json解析为com.amazonaws.services.lambda.runtime.events.S3EventNotification

https://github.com/aws/aws-lambda-java-libs/issues/151

阅读JSON之后,我得到了 com.amazonaws.services.s3.event.S3EventNotification 但是FunctionHandler需要S3Event从 com.amazonaws.services.lambda.runtime.events.S3Event

我无法S3Event(notification.records)

所以我不得不编写自己的方法来转换类

    fun test() {
        val fileContent = this::class.java.classLoader.getResource("s3-event.json").readText()
        val s3NotificationEvent = ObjectMapper().registerKotlinModule().readValue(fileContent, S3EventNotification::class.java)
        val s3Event = S3Event(
            s3NotificationEvent.records.map {
                it.toRuntimeS3NotificationRecord()
            }
        )
    )

fun S3EventNotification.S3EventNotificationRecord.toRuntimeS3NotificationRecord(): com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord {
return com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3EventNotificationRecord(
    awsRegion, eventName, eventSource,
    eventTime.toString(), eventVersion,
    com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.RequestParametersEntity(requestParameters.sourceIPAddress),
    com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.ResponseElementsEntity(responseElements.getxAmzId2(), responseElements.getxAmzRequestId()),
    com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3Entity(
        s3.configurationId,
        com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3BucketEntity(
            s3.bucket.name,
            com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.UserIdentityEntity(s3.bucket.ownerIdentity.principalId),
            s3.bucket.arn
        ),
        com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.S3ObjectEntity(s3.`object`.key, s3.`object`.sizeAsLong, s3.`object`.geteTag(), s3.`object`.versionId, s3.`object`.sequencer),
        s3.s3SchemaVersion
    ),
    com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification.UserIdentityEntity(userIdentity.principalId)
)

}