|
| 1 | + |
| 2 | +# Tests utility |
| 3 | + |
| 4 | +The `aws-lambda-java-tests` module provides opinionated tools to ease Java Lambda testing. This is a test dependency. |
| 5 | + |
| 6 | +**Key features** |
| 7 | + |
| 8 | +* Load events from json files and get them deserialized into Java Events. |
| 9 | +* Inject Events directly in JUnit 5 tests, using the `@ParameterizedTest` annotation. |
| 10 | + |
| 11 | + |
| 12 | +## Background |
| 13 | + |
| 14 | +When using Java for a Lambda function, you must implement the RequestHandler interface and provide input and output types: |
| 15 | + |
| 16 | +```java |
| 17 | +public interface RequestHandler<I, O> { |
| 18 | + public O handleRequest(I input, Context context); |
| 19 | +} |
| 20 | +``` |
| 21 | + |
| 22 | +The input is automatically deserialized by the Lambda Java Runtime from a json event into the type you define, |
| 23 | +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). |
| 24 | + |
| 25 | +When you want to test your Lambda function and your handleRequest method, you cannot simply use JSON events files |
| 26 | +as some of the event fields may not be deserialized correctly. |
| 27 | + |
| 28 | +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. |
| 29 | +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 |
| 30 | +in the doc, the Lambda console or in your logs. |
| 31 | + |
| 32 | +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. |
| 33 | + |
| 34 | +## Installation |
| 35 | + |
| 36 | +To install this utility, add the following dependency to your project. Note that it's a test dependency. |
| 37 | + |
| 38 | +```xml |
| 39 | +<dependency> |
| 40 | + <groupId>com.amazonaws</groupId> |
| 41 | + <artifactId>aws-lambda-java-tests</artifactId> |
| 42 | + <version>1.0.0</version> |
| 43 | + <scope>test</scope> |
| 44 | +</dependency> |
| 45 | +``` |
| 46 | + |
| 47 | +Also have surefire in your plugins: |
| 48 | + |
| 49 | +```xml |
| 50 | +<build> |
| 51 | + <plugins> |
| 52 | + <plugin> |
| 53 | + <groupId>org.apache.maven.plugins</groupId> |
| 54 | + <artifactId>maven-surefire-plugin</artifactId> |
| 55 | + <version>2.22.2</version> |
| 56 | + </plugin> |
| 57 | + </plugins> |
| 58 | +</build> |
| 59 | +``` |
| 60 | + |
| 61 | +## Usage |
| 62 | + |
| 63 | +### Events injection |
| 64 | + |
| 65 | +A set of annotations can be used to inject Events and/or to validate handler responses against those Events. |
| 66 | +**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.** |
| 67 | + |
| 68 | +`ParameterizedTest` enables to inject arguments into a unit test, so you can run the same test one or more time with different parameters. |
| 69 | +See [the doc](https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests) for more details on this. |
| 70 | + |
| 71 | +**Event** |
| 72 | + |
| 73 | +The `@Event` annotation permits to inject one Event into a Junit test. |
| 74 | + |
| 75 | +Example: |
| 76 | + |
| 77 | +```java |
| 78 | +// the json file must be in the classpath (most often in src/test/resources) |
| 79 | +@ParameterizedTest |
| 80 | +@Event(value = "sqs/sqs_event.json", type = SQSEvent.class) |
| 81 | +public void testInjectSQSEvent(SQSEvent event) { |
| 82 | + // test your handleRequest method with this event as parameter |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +**Events** |
| 87 | + |
| 88 | +The `@Events` annotation permits to inject multiple Events into a Junit test |
| 89 | + |
| 90 | +Examples: |
| 91 | + |
| 92 | +```java |
| 93 | +@ParameterizedTest |
| 94 | +@Events( |
| 95 | + events = { |
| 96 | + @Event("sqs/sqs_event.json"), |
| 97 | + @Event("sqs/sqs_event2.json"), |
| 98 | + }, |
| 99 | + type = SQSEvent.class |
| 100 | +) |
| 101 | +public void testInjectEvents(SQSEvent event) { |
| 102 | + // test your handleRequest method with all the JSON events available in the sqs folder |
| 103 | +} |
| 104 | + |
| 105 | +// OR simpler |
| 106 | + |
| 107 | +// sqs folder must be in the classpath (most often in src/test/resources) |
| 108 | +@ParameterizedTest |
| 109 | +@Events(folder = "sqs", type = SQSEvent.class) |
| 110 | +public void testInjectEventsFromFolder(SQSEvent event) { |
| 111 | + // test your handleRequest method with all the JSON events available in the sqs folder |
| 112 | +} |
| 113 | +``` |
| 114 | + |
| 115 | +**HandlerParams** |
| 116 | + |
| 117 | +The `@HandlerParams` is the most advanced one as it permits to provide both input and output as arguments to your tests. |
| 118 | +Thus you can validate your `handlerRequest` method by providing the output and asserting on the expected output. |
| 119 | + |
| 120 | +```java |
| 121 | + |
| 122 | +// Single event |
| 123 | +@ParameterizedTest |
| 124 | +@HandlerParams( |
| 125 | + event = @Event(value = "apigw/events/apigw_event.json", type = APIGatewayProxyRequestEvent.class), |
| 126 | + response = @Response(value = "apigw/responses/apigw_response.json", type = APIGatewayProxyResponseEvent.class)) |
| 127 | +public void testSingleEventResponse(APIGatewayProxyRequestEvent event, APIGatewayProxyResponseEvent response) { |
| 128 | +} |
| 129 | + |
| 130 | +// Multiple events in folder |
| 131 | +@ParameterizedTest |
| 132 | +@HandlerParams( |
| 133 | + events = @Events(folder = "apigw/events/", type = APIGatewayProxyRequestEvent.class), |
| 134 | + responses = @Responses(folder = "apigw/responses/", type = APIGatewayProxyResponseEvent.class)) |
| 135 | +public void testMultipleEventsResponsesInFolder(APIGatewayProxyRequestEvent event, APIGatewayProxyResponseEvent response) { |
| 136 | +} |
| 137 | + |
| 138 | +// Multiple events |
| 139 | +@HandlerParams( |
| 140 | + events = @Events( |
| 141 | + events = { |
| 142 | + @Event("apigw/events/apigw_event.json"), |
| 143 | + @Event("apigw/events/apigw_event2.json"), |
| 144 | + }, |
| 145 | + type = APIGatewayProxyRequestEvent.class |
| 146 | + ), |
| 147 | + responses = @Responses( |
| 148 | + responses = { |
| 149 | + @Response("apigw/responses/apigw_response.json"), |
| 150 | + @Response("apigw/responses/apigw_response2.json") |
| 151 | + }, |
| 152 | + type = APIGatewayProxyResponseEvent.class |
| 153 | + ) |
| 154 | +) |
| 155 | +public void testMultipleEventsResponses(APIGatewayProxyRequestEvent event, APIGatewayProxyResponseEvent response) { |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +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. |
| 160 | + |
| 161 | +### EventLoader |
| 162 | + |
| 163 | +`EventLoader` enables to load any Event from a JSON file and deserialize it into a Java Object. |
| 164 | +Either one from the [aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/master/aws-lambda-java-events) library |
| 165 | +or your own Event. |
| 166 | + |
| 167 | +EventLoader provides a load method for most of the pre-defined events: |
| 168 | + |
| 169 | +```java |
| 170 | +APIGatewayV2HTTPEvent httpEvent = |
| 171 | + EventLoader.loadApiGatewayHttpEvent("apigw_http_event.json"); |
| 172 | + |
| 173 | +APIGatewayProxyRequestEvent restEvent = |
| 174 | + EventLoader.loadApiGatewayRestEvent("apigw_rest_event.json"); |
| 175 | + |
| 176 | +DynamodbEvent ddbEvent = EventLoader.loadDynamoDbEvent("ddb_event.json"); |
| 177 | + |
| 178 | +KinesisEvent kinesisEvent = |
| 179 | + EventLoader.loadKinesisEvent("kinesis_event.json"); |
| 180 | + |
| 181 | +ScheduledEvent eventBridgeEvent = |
| 182 | + EventLoader.loadScheduledEvent("eb_event.json"); |
| 183 | + |
| 184 | +S3Event s3Event = EventLoader.loadS3Event("s3_event.json"); |
| 185 | + |
| 186 | +SNSEvent snsEvent = EventLoader.loadSNSEvent("sns_event.json"); |
| 187 | + |
| 188 | +SQSEvent sqsEvent = EventLoader.loadSQSEvent("sqs_event.json"); |
| 189 | + |
| 190 | +// ... and many others |
| 191 | +``` |
| 192 | + |
| 193 | +Or you can load what you prefer with the generic method: |
| 194 | + |
| 195 | +```java |
| 196 | +MyEvent myEvent = EventLoader.loadEvent("my_event.json", MyEvent.class); |
| 197 | +``` |
| 198 | + |
0 commit comments