diff --git a/.github/workflows/aws-lambda-java-events.yml b/.github/workflows/aws-lambda-java-events.yml
index 6b7e2f81..df2a06be 100644
--- a/.github/workflows/aws-lambda-java-events.yml
+++ b/.github/workflows/aws-lambda-java-events.yml
@@ -34,4 +34,4 @@ jobs:
run: mvn -B package --file aws-lambda-java-serialization/pom.xml
- name: Build events-sdk-transformer with Maven
run: mvn -B package --file aws-lambda-java-events-sdk-transformer/pom.xml
-
+
diff --git a/.github/workflows/aws-lambda-java-tests.yml b/.github/workflows/aws-lambda-java-tests.yml
new file mode 100644
index 00000000..b00b3781
--- /dev/null
+++ b/.github/workflows/aws-lambda-java-tests.yml
@@ -0,0 +1,37 @@
+# This workflow will be triggered if there will be changes to aws-lambda-java-tests
+# package and it builds the package and the packages that depend on it.
+
+name: Java CI aws-lambda-java-tests
+
+on:
+ push:
+ branches: [ master ]
+ paths:
+ - 'aws-lambda-java-tests/**'
+ pull_request:
+ branches: [ '*' ]
+ paths:
+ - 'aws-lambda-java-tests/**'
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+
+ # Install base module
+ - name: Install events with Maven
+ run: mvn -B install --file aws-lambda-java-events/pom.xml
+ - name: Install serialization with Maven
+ run: mvn -B install --file aws-lambda-java-serialization/pom.xml
+
+ # Package target module
+ - name: Package tests with Maven
+ run: mvn -B package --file aws-lambda-java-tests/pom.xml
+
diff --git a/README.md b/README.md
index 44215219..3c386922 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ Check out the per-module release notes:
- [aws-lambda-java-log4j2](aws-lambda-java-log4j2/RELEASE.CHANGELOG.md)
- [aws-lambda-java-runtime-interface-client](aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md)
- [aws-lambda-java-serialization](aws-lambda-java-serialization/RELEASE.CHANGELOG.md)
+- [aws-lambda-java-test](aws-lambda-java-tests/RELEASE.CHANGELOG.md)
# Where to get packages
___
@@ -53,6 +54,12 @@ ___
aws-lambda-java-runtime-interface-client1.0.0
+
+ com.amazonaws
+ aws-lambda-java-tests
+ 1.0.0
+ test
+
```
[Gradle](https://gradle.org)
@@ -63,6 +70,7 @@ ___
'com.amazonaws:aws-lambda-java-events-sdk-transformer:3.0.1'
'com.amazonaws:aws-lambda-java-log4j2:1.2.0'
'com.amazonaws:aws-lambda-java-runtime-interface-client:1.0.0'
+'com.amazonaws:aws-lambda-java-tests:1.0.0'
```
[Leiningen](http://leiningen.org) and [Boot](http://boot-clj.com)
@@ -73,6 +81,7 @@ ___
[com.amazonaws/aws-lambda-java-events-sdk-transformer "3.0.1"]
[com.amazonaws/aws-lambda-java-log4j2 "1.2.0"]
[com.amazonaws/aws-lambda-java-runtime-interface-client "1.0.0"]
+[com.amazonaws/aws-lambda-java-tests "1.0.0"]
```
[sbt](http://www.scala-sbt.org)
@@ -83,6 +92,7 @@ ___
"com.amazonaws" % "aws-lambda-java-events-sdk-transformer" % "3.0.1"
"com.amazonaws" % "aws-lambda-java-log4j2" % "1.2.0"
"com.amazonaws" % "aws-lambda-java-runtime-interface-client" % "1.0.0"
+"com.amazonaws" % "aws-lambda-java-tests" % "1.0.0"
```
# Using aws-lambda-java-core
@@ -114,3 +124,7 @@ The purpose of this package is to allow developers to deploy their applications
# Using aws-lambda-java-serialization
This package defines the Lambda serialization logic using in the aws-lambda-java-runtime-client library. It has no current standalone usage.
+
+# Using aws-lambda-java-tests
+
+This package provides utils to ease Lambda Java testing. Used with `aws-lambda-java-serialization` and `aws-lambda-java-events` to inject events in your JUnit tests.
diff --git a/aws-lambda-java-tests/README.md b/aws-lambda-java-tests/README.md
new file mode 100644
index 00000000..780717fe
--- /dev/null
+++ b/aws-lambda-java-tests/README.md
@@ -0,0 +1,198 @@
+
+# Tests utility
+
+The `aws-lambda-java-tests` module provides opinionated tools to ease Java Lambda testing. This is a test dependency.
+
+**Key features**
+
+* Load events from json files and get them deserialized into Java Events.
+* Inject Events directly in JUnit 5 tests, using the `@ParameterizedTest` annotation.
+
+
+## Background
+
+When using Java for a Lambda function, you must implement the RequestHandler interface and provide input and output types:
+
+```java
+public interface RequestHandler {
+ public O handleRequest(I input, Context context);
+}
+```
+
+The input is automatically deserialized by the Lambda Java Runtime from a json event into the type you define,
+and the output is serialized into JSON from the output type. More info in the [docs](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html).
+
+When you want to test your Lambda function and your handleRequest method, you cannot simply use JSON events files
+as some of the event fields may not be deserialized correctly.
+
+For example, an SQS JSON event contains a list of "Records", while the [`SQSEvent`](https://github.com/aws/aws-lambda-java-libs/blob/master/aws-lambda-java-events/src/main/java/com/amazonaws/services/lambda/runtime/events/SQSEvent.java) use "records" with a lowercase.
+You can choose to modify the JSON input but it can be tedious and you generally want to keep the JSON event as you get it
+in the doc, the Lambda console or in your logs.
+
+Now you can use the [aws-lambda-java-serialization](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-serialization) module to deserialize events. And this test library is using this module as a dependency to ease tests of lambda function handlers.
+
+## Installation
+
+To install this utility, add the following dependency to your project. Note that it's a test dependency.
+
+```xml
+
+ com.amazonaws
+ aws-lambda-java-tests
+ 1.0.0
+ test
+
+```
+
+Also have surefire in your plugins:
+
+```xml
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+
+
+```
+
+## Usage
+
+### Events injection
+
+A set of annotations can be used to inject Events and/or to validate handler responses against those Events.
+**All those annotations must be used in conjunction with the [`@ParameterizedTest`](https://junit.org/junit5/docs/current/api/org.junit.jupiter.params/org/junit/jupiter/params/ParameterizedTest.html) annotation from Junit 5.**
+
+`ParameterizedTest` enables to inject arguments into a unit test, so you can run the same test one or more time with different parameters.
+See [the doc](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests) for more details on this.
+
+**Event**
+
+The `@Event` annotation permits to inject one Event into a Junit test.
+
+Example:
+
+```java
+// the json file must be in the classpath (most often in src/test/resources)
+@ParameterizedTest
+@Event(value = "sqs/sqs_event.json", type = SQSEvent.class)
+public void testInjectSQSEvent(SQSEvent event) {
+ // test your handleRequest method with this event as parameter
+}
+```
+
+**Events**
+
+The `@Events` annotation permits to inject multiple Events into a Junit test
+
+Examples:
+
+```java
+@ParameterizedTest
+@Events(
+ events = {
+ @Event("sqs/sqs_event.json"),
+ @Event("sqs/sqs_event2.json"),
+ },
+ type = SQSEvent.class
+)
+public void testInjectEvents(SQSEvent event) {
+ // test your handleRequest method with all the JSON events available in the sqs folder
+}
+
+// OR simpler
+
+// sqs folder must be in the classpath (most often in src/test/resources)
+@ParameterizedTest
+@Events(folder = "sqs", type = SQSEvent.class)
+public void testInjectEventsFromFolder(SQSEvent event) {
+ // test your handleRequest method with all the JSON events available in the sqs folder
+}
+```
+
+**HandlerParams**
+
+The `@HandlerParams` is the most advanced one as it permits to provide both input and output as arguments to your tests.
+Thus you can validate your `handlerRequest` method by providing the output and asserting on the expected output.
+
+```java
+
+// Single event
+@ParameterizedTest
+@HandlerParams(
+ event = @Event(value = "apigw/events/apigw_event.json", type = APIGatewayProxyRequestEvent.class),
+ response = @Response(value = "apigw/responses/apigw_response.json", type = APIGatewayProxyResponseEvent.class))
+public void testSingleEventResponse(APIGatewayProxyRequestEvent event, APIGatewayProxyResponseEvent response) {
+}
+
+// Multiple events in folder
+@ParameterizedTest
+@HandlerParams(
+ events = @Events(folder = "apigw/events/", type = APIGatewayProxyRequestEvent.class),
+ responses = @Responses(folder = "apigw/responses/", type = APIGatewayProxyResponseEvent.class))
+public void testMultipleEventsResponsesInFolder(APIGatewayProxyRequestEvent event, APIGatewayProxyResponseEvent response) {
+}
+
+// Multiple events
+@HandlerParams(
+ events = @Events(
+ events = {
+ @Event("apigw/events/apigw_event.json"),
+ @Event("apigw/events/apigw_event2.json"),
+ },
+ type = APIGatewayProxyRequestEvent.class
+ ),
+ responses = @Responses(
+ responses = {
+ @Response("apigw/responses/apigw_response.json"),
+ @Response("apigw/responses/apigw_response2.json")
+ },
+ type = APIGatewayProxyResponseEvent.class
+ )
+)
+public void testMultipleEventsResponses(APIGatewayProxyRequestEvent event, APIGatewayProxyResponseEvent response) {
+}
+```
+
+If you cannot use those annotations (for example if you use TestNG), or if you want to load the events on your own, you can directly use the `EventLoader`, which is the underlying class that load the json events.
+
+### EventLoader
+
+`EventLoader` enables to load any Event from a JSON file and deserialize it into a Java Object.
+Either one from the [aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-events) library
+or your own Event.
+
+EventLoader provides a load method for most of the pre-defined events:
+
+```java
+APIGatewayV2HTTPEvent httpEvent =
+ EventLoader.loadApiGatewayHttpEvent("apigw_http_event.json");
+
+APIGatewayProxyRequestEvent restEvent =
+ EventLoader.loadApiGatewayRestEvent("apigw_rest_event.json");
+
+DynamodbEvent ddbEvent = EventLoader.loadDynamoDbEvent("ddb_event.json");
+
+KinesisEvent kinesisEvent =
+ EventLoader.loadKinesisEvent("kinesis_event.json");
+
+ScheduledEvent eventBridgeEvent =
+ EventLoader.loadScheduledEvent("eb_event.json");
+
+S3Event s3Event = EventLoader.loadS3Event("s3_event.json");
+
+SNSEvent snsEvent = EventLoader.loadSNSEvent("sns_event.json");
+
+SQSEvent sqsEvent = EventLoader.loadSQSEvent("sqs_event.json");
+
+// ... and many others
+```
+
+Or you can load what you prefer with the generic method:
+
+```java
+MyEvent myEvent = EventLoader.loadEvent("my_event.json", MyEvent.class);
+```
+
diff --git a/aws-lambda-java-tests/RELEASE.CHANGELOG.md b/aws-lambda-java-tests/RELEASE.CHANGELOG.md
new file mode 100644
index 00000000..60b92247
--- /dev/null
+++ b/aws-lambda-java-tests/RELEASE.CHANGELOG.md
@@ -0,0 +1,3 @@
+### December 03, 2020
+`1.0.0`:
+- Initial release of AWS Lambda Java Tests
\ No newline at end of file
diff --git a/aws-lambda-java-tests/pom.xml b/aws-lambda-java-tests/pom.xml
new file mode 100644
index 00000000..0b734a9b
--- /dev/null
+++ b/aws-lambda-java-tests/pom.xml
@@ -0,0 +1,256 @@
+
+ 4.0.0
+
+ com.amazonaws
+ aws-lambda-java-tests
+ 1.0.0
+ jar
+
+ AWS Lambda Java Tests
+ Testing module for the AWS Lambda Java Runtime
+ https://aws.amazon.com/lambda/
+
+
+ Apache License, Version 2.0
+ https://aws.amazon.com/apache2.0
+ repo
+
+
+
+ https://github.com/aws/aws-lambda-java-libs.git
+
+
+
+ AWS Lambda team
+ Amazon Web Services
+ https://aws.amazon.com/
+
+
+
+
+ 1.8
+ 1.8
+ UTF-8
+ 5.7.0
+
+
+
+
+ com.amazonaws
+ aws-lambda-java-serialization
+ 1.0.0
+
+
+ com.amazonaws
+ aws-lambda-java-events
+ 3.6.0
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit.version}
+
+
+ org.apache.commons
+ commons-lang3
+ 3.11
+
+
+
+ org.assertj
+ assertj-core
+ 3.18.1
+ test
+
+
+
+
+
+ checkDependencies
+
+
+ checkDependencies
+
+
+
+
+
+ org.owasp
+ dependency-check-maven
+ 5.3.2
+
+
+ validate
+
+ check
+
+
+
+
+
+
+
+
+ dev
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ none
+ false
+ 8
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.6
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ default-report
+ test
+
+ report
+
+
+
+ default-check
+ test
+
+ check
+
+
+
+
+ PACKAGE
+
+
+ LINE
+ COVEREDRATIO
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.2.0
+
+ none
+ false
+ 8
+ false
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.3
+ true
+
+ sonatype-nexus-staging
+ https://aws.oss.sonatype.org/
+ false
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ false
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.22.2
+
+
+
+
\ No newline at end of file
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventArgumentsProvider.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventArgumentsProvider.java
new file mode 100644
index 00000000..0dec6e6b
--- /dev/null
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventArgumentsProvider.java
@@ -0,0 +1,29 @@
+/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+package com.amazonaws.services.lambda.runtime.tests;
+
+import com.amazonaws.services.lambda.runtime.tests.annotations.Event;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.support.AnnotationConsumer;
+
+import java.util.stream.Stream;
+
+/**
+ * Used to process @{@link Event} com.amazonaws.services.lambda.runtime.tests.annotations
+ */
+public class EventArgumentsProvider implements ArgumentsProvider, AnnotationConsumer {
+
+ private Event event;
+
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) {
+ Object o = EventLoader.loadEvent(event.value(), event.type());
+ return Stream.of(Arguments.of(o));
+ }
+
+ @Override
+ public void accept(Event event) {
+ this.event = event;
+ }
+}
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
new file mode 100644
index 00000000..0216d5e3
--- /dev/null
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoader.java
@@ -0,0 +1,137 @@
+/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+package com.amazonaws.services.lambda.runtime.tests;
+
+import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
+import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
+
+import java.io.*;
+
+import com.amazonaws.services.lambda.runtime.events.*;
+
+/**
+ * Load events from json files and serialize them in Events
+ */
+public class EventLoader {
+
+ public static ActiveMQEvent loadActiveMQEvent(String filename) {
+ return loadEvent(filename, ActiveMQEvent.class);
+ }
+
+ public static APIGatewayV2HTTPEvent loadApiGatewayHttpEvent(String filename) {
+ return loadEvent(filename, APIGatewayV2HTTPEvent.class);
+ }
+
+ public static APIGatewayProxyRequestEvent loadApiGatewayRestEvent(String filename) {
+ return loadEvent(filename, APIGatewayProxyRequestEvent.class);
+ }
+
+ public static APIGatewayCustomAuthorizerEvent loadAPIGatewayCustomAuthorizerEvent(String filename) {
+ return loadEvent(filename, APIGatewayCustomAuthorizerEvent.class);
+ }
+
+ public static APIGatewayV2CustomAuthorizerEvent loadAPIGatewayV2CustomAuthorizerEvent(String filename) {
+ return loadEvent(filename, APIGatewayV2CustomAuthorizerEvent.class);
+ }
+
+ public static ApplicationLoadBalancerRequestEvent loadApplicationLoadBalancerRequestEvent(String filename) {
+ return loadEvent(filename, ApplicationLoadBalancerRequestEvent.class);
+ }
+
+ public static CloudFormationCustomResourceEvent loadCloudFormationCustomResourceEvent(String filename) {
+ return loadEvent(filename, CloudFormationCustomResourceEvent.class);
+ }
+
+ public static CloudFrontEvent loadCloudFrontEvent(String filename) {
+ return loadEvent(filename, CloudFrontEvent.class);
+ }
+
+ public static CloudWatchLogsEvent loadCloudWatchLogsEvent(String filename) {
+ return loadEvent(filename, CloudWatchLogsEvent.class);
+ }
+
+ public static CodeCommitEvent loadCodeCommitEvent(String filename) {
+ return loadEvent(filename, CodeCommitEvent.class);
+ }
+
+ public static ConfigEvent loadConfigEvent(String filename) {
+ return loadEvent(filename, ConfigEvent.class);
+ }
+
+ public static ConnectEvent loadConnectEvent(String filename) {
+ return loadEvent(filename, ConnectEvent.class);
+ }
+
+ public static DynamodbEvent loadDynamoDbEvent(String filename) {
+ return loadEvent(filename, DynamodbEvent.class);
+ }
+
+ public static KafkaEvent loadKafkaEvent(String filename) {
+ return loadEvent(filename, KafkaEvent.class);
+ }
+
+ public static KinesisEvent loadKinesisEvent(String filename) {
+ return loadEvent(filename, KinesisEvent.class);
+ }
+
+ public static KinesisFirehoseEvent loadKinesisFirehoseEvent(String filename) {
+ return loadEvent(filename, KinesisFirehoseEvent.class);
+ }
+
+ public static LambdaDestinationEvent loadLambdaDestinationEvent(String filename) {
+ return loadEvent(filename, LambdaDestinationEvent.class);
+ }
+
+ public static LexEvent loadLexEvent(String filename) {
+ return loadEvent(filename, LexEvent.class);
+ }
+
+ public static S3Event loadS3Event(String filename) {
+ return loadEvent(filename, S3Event.class);
+ }
+
+ public static SecretsManagerRotationEvent loadSecretsManagerRotationEvent(String filename) {
+ return loadEvent(filename, SecretsManagerRotationEvent.class);
+ }
+
+ public static ScheduledEvent loadScheduledEvent(String filename) {
+ return loadEvent(filename, ScheduledEvent.class);
+ }
+
+ public static SNSEvent loadSNSEvent(String filename) {
+ return loadEvent(filename, SNSEvent.class);
+ }
+
+ public static SQSEvent loadSQSEvent(String filename) {
+ return loadEvent(filename, SQSEvent.class);
+ }
+
+ public static T loadEvent(String filename, Class targetClass) {
+
+ if (!filename.endsWith("json")) {
+ throw new IllegalArgumentException("File " + filename + " must have json extension");
+ }
+
+ PojoSerializer serializer = LambdaEventSerializers.serializerFor(targetClass, ClassLoader.getSystemClassLoader());
+
+ InputStream stream = serializer.getClass().getResourceAsStream(filename);
+ if (stream == null) {
+ stream = serializer.getClass().getClassLoader().getResourceAsStream(filename);
+ }
+ if (stream == null) {
+ try {
+ stream = new FileInputStream(new File(filename));
+ } catch (FileNotFoundException e) {
+ throw new EventLoadingException("Cannot load " + filename, e);
+ }
+ }
+ try {
+ return serializer.fromJson(stream);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoadingException.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoadingException.java
new file mode 100644
index 00000000..c7b8c34d
--- /dev/null
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventLoadingException.java
@@ -0,0 +1,18 @@
+/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+package com.amazonaws.services.lambda.runtime.tests;
+
+public class EventLoadingException extends RuntimeException {
+
+ private static final long serialVersionUID = 5766526909472206270L;
+
+ public EventLoadingException() {
+ }
+
+ public EventLoadingException(String message) {
+ super(message);
+ }
+
+ public EventLoadingException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventsArgumentsProvider.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventsArgumentsProvider.java
new file mode 100644
index 00000000..2a3b93df
--- /dev/null
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/EventsArgumentsProvider.java
@@ -0,0 +1,52 @@
+/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+package com.amazonaws.services.lambda.runtime.tests;
+
+import com.amazonaws.services.lambda.runtime.tests.annotations.Events;
+import org.apache.commons.lang3.ArrayUtils;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.support.AnnotationConsumer;
+
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+/**
+ * Used to process @{@link Events} com.amazonaws.services.lambda.runtime.tests.annotations
+ */
+public class EventsArgumentsProvider implements ArgumentsProvider, AnnotationConsumer {
+
+ private Events events;
+
+ @Override
+ public void accept(Events events) {
+ this.events = events;
+ }
+
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+ if (ArrayUtils.isNotEmpty(events.events())) {
+ return Arrays.stream(events.events())
+ .map(event -> {
+ Class> clazz = event.type() == Void.class ? events.type() : event.type();
+ return Arguments.of(EventLoader.loadEvent(event.value(), clazz));
+ });
+ } else {
+ URL folderUrl = getClass().getResource(events.folder());
+ if (folderUrl == null) {
+ folderUrl = getClass().getClassLoader().getResource(events.folder());
+ }
+ if (folderUrl == null) {
+ throw new IllegalArgumentException("Path " + events.folder() + " cannot be found");
+ }
+ Stream files = Files.list(Paths.get(folderUrl.toURI()));
+ return files
+ .filter(Files::isRegularFile)
+ .map(path -> Arguments.of(EventLoader.loadEvent(path.toString(), events.type())));
+ }
+ }
+}
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/HandlerParamsArgumentsProvider.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/HandlerParamsArgumentsProvider.java
new file mode 100644
index 00000000..269180dc
--- /dev/null
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/HandlerParamsArgumentsProvider.java
@@ -0,0 +1,130 @@
+/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+package com.amazonaws.services.lambda.runtime.tests;
+
+import com.amazonaws.services.lambda.runtime.tests.annotations.*;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.support.AnnotationConsumer;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Used to process @{@link HandlerParams} com.amazonaws.services.lambda.runtime.tests.annotations
+ */
+public class HandlerParamsArgumentsProvider implements ArgumentsProvider, AnnotationConsumer {
+
+ private Event event;
+ private Response response;
+
+ private Events events;
+ private Responses responses;
+
+ @Override
+ public void accept(HandlerParams handlerParams) {
+ this.event = handlerParams.event();
+ this.response = handlerParams.response();
+ this.events = handlerParams.events();
+ this.responses = handlerParams.responses();
+ }
+
+ @Override
+ public Stream extends Arguments> provideArguments(ExtensionContext context) throws Exception {
+ if ((!event.value().isEmpty() && response.value().isEmpty()) ||
+ (event.value().isEmpty() && !response.value().isEmpty())) {
+ throw new IllegalStateException("You must use either Event & Response (singular) or Events & Responses (plural) annotations together, you cannot mix them");
+ }
+ if (((ArrayUtils.isEmpty(events.events()) && StringUtils.isEmpty(events.folder()))
+ && (StringUtils.isNotEmpty(responses.folder()) || ArrayUtils.isNotEmpty(responses.responses())))
+ ||
+ ((ArrayUtils.isEmpty(responses.responses()) && StringUtils.isEmpty(responses.folder()))
+ && (StringUtils.isNotEmpty(events.folder()) || ArrayUtils.isNotEmpty(events.events())))) {
+ throw new IllegalStateException("You must use either Event & Response (singular) or Events & Responses (plural) annotations together, you cannot mix them");
+ }
+
+ // deal with one element
+ if (!event.value().isEmpty() && !response.value().isEmpty()) {
+ return Stream.of(
+ Arguments.of(
+ EventLoader.loadEvent(event.value(), event.type()),
+ EventLoader.loadEvent(response.value(), response.type())
+ )
+ );
+ }
+
+ // deal with many elements
+ List
+ */
+@Documented
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+@ArgumentsSource(EventsArgumentsProvider.class)
+public @interface Events {
+
+ /**
+ * Folder where to find json files containing events
+ * @return the folder name
+ */
+ String folder() default "";
+
+ /**
+ * Type of the events (for example, one of the aws-lambda-java-events), or your own type
+ * @return the type of the events
+ */
+ Class> type() default Void.class;
+
+ /**
+ * Mutually exclusive with folder
+ * @return the array of events
+ */
+ Event[] events() default {};
+}
diff --git a/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/annotations/HandlerParams.java b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/annotations/HandlerParams.java
new file mode 100644
index 00000000..3e656cc9
--- /dev/null
+++ b/aws-lambda-java-tests/src/main/java/com/amazonaws/services/lambda/runtime/tests/annotations/HandlerParams.java
@@ -0,0 +1,63 @@
+/* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+package com.amazonaws.services.lambda.runtime.tests.annotations;
+
+import com.amazonaws.services.lambda.runtime.tests.HandlerParamsArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+
+import java.lang.annotation.*;
+
+/**
+ * This annotation must be used in conjunction with {@link org.junit.jupiter.params.ParameterizedTest}.
+ * It enables to inject Events and Responses into the current test.
+ * Either use the {@link #event()} and {@link #response()} for a single event/response
+ * or {@link #events()} and {@link #responses()} for multiple ones.
+ *
+ * Example:
+ *