1. Overview

AWS TestKit provides a set of JUnit 5 extensions to make testing with AWS SDK easier. AWS TestKit works with service mocks provided by tools like LocalStack, DynamoDB Local, or even real AWS services.

Only AWS SDK for Java 2.x is supported. Version 1.x will not be supported.

Currently, the following services are supported:

  • CloudFormation

  • DynamoDB

  • Firehose

  • Kinesis

  • KMS

  • S3

  • SecretsManager

  • SES

  • SNS

  • SQS

2. Configuration

AWS TestKit provides an endpoint resolver mechanism to resolve endpoint configuration values for the AWS SDK clients. To provide values for configuration you need to implement an EndpointResolver. However, if you are using the LocalStack extension, the endpoint configuration can be omitted.

Providing endpoint configuration as environment variables
import com.github.awstestkit.Endpoint;
import com.github.awstestkit.EndpointResolver;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.extension.ExtensionContext;

import java.net.URI;
import java.net.URISyntaxException;

public class MyEndpointResolver implements EndpointResolver {
    @NotNull
    @Override
    public Endpoint resolveEndpoint(@NotNull ExtensionContext extensionContext) {
        return new Endpoint() {
            @NotNull
            @Override
            public URI url() throws URISyntaxException {
                return new URI(System.getenv("AWS_URL"));
            }

            @NotNull
            @Override
            public String region() {
                return System.getenv("AWS_REGION");
            }

            @NotNull
            @Override
            public String accessKey() {
                return System.getenv("AWS_ACCESS_KEY");
            }

            @NotNull
            @Override
            public String secretKey() {
                return System.getenv("AWS_SECRET_KEY");
            }
        };
    }
}

The endpoint resolver has to be added to the test using the @AwsEndpoint annotation. To inject AWS SDK clients to the test you can use the @AwsClient annotation.

Configuring your custom endpoint
import com.github.awstestkit.AwsClient;
import com.github.awstestkit.AwsEndpoint;
import com.github.awstestkit.sns.junit5.SnsTest;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.services.sns.SnsClient;

import static java.util.Collections.emptyList;
import static org.junit.jupiter.api.Assertions.assertIterableEquals;

@SnsTest
@AwsEndpoint(endpointResolver = MyEndpointResolver.class)
class CustomEndpointTest {
    @Test
    void getTopics(@AwsClient SnsClient client) {
        assertIterableEquals(client.listTopics().topics(), emptyList());
    }
}

The client injection only works if you have annotated the test with one of service test annotations such as @SnsTest.

3. LocalStack Extension

Using the @LocalStackTest annotation will start up LocalStack using Testcontainers for your tests. You can pass the required services as an argument.

If any of the service extensions (e.g. @DynamoDbTest, @SnsTest) has been provided, then the services argument can be omitted.

Using the LocalStack extension
@LocalStackTest
@SnsTest
class LocalStackExtensionTest {
    @Test
    void localStackIsRunning(@AwsClient SnsClient client) {
        assertIterableEquals(client.listTopics().topics(), emptyList());
    }
}

You don’t need to configure an endpoint resolver when using the LocalStack extension. The extension will automatically configure an LocalStackEndpointResolver that works with the LocalStack container it starts.

4. CloudFormation Extension

The CloudFormation extension allows you to create CloudFormation stacks inside your tests. If you already have your infrastructure described as CloudFormation templates you can then reuse some of those templates in your tests.

Using the CloudFormation extension
@LocalStackTest
@CloudFormationTest
@CfnStacks(
        @CfnStack(name = "awstestkit-stack", templateFile = "src/test/resources/template.yml")
)
class CloudFormationExtensionTest {
    @Test
    void setupStacks(@AwsClient CloudFormationClient client) {
        assertFalse(client.listStacks().stackSummaries().isEmpty());
    }
}

5. DynamoDB Extension

The DynamoDB extension allows you to create DynamoDB tables inside your tests.

Using the DynamoDB extension
@LocalStackTest
@DynamoDbTest
@DynamoDbTables(
        @DynamoDbTable(
                tableName = "table",
                keySchema = @DynamoDbKeySchemaElement(
                        attributeName = "id",
                        keyType = KeyType.HASH
                ),
                attributeDefinitions = @DynamoDbAttributeDefinition(
                        attributeName = "id",
                        attributeType = ScalarAttributeType.S
                )
        )
)
class DynamoDbExtensionTest {
    @Test
    void setupTables(@AwsClient DynamoDbClient client) {
        assertFalse(client.listTables().tableNames().isEmpty());
    }
}

6. S3 Extension

The S3 extension allows you to create S3 buckets and objects inside your tests.

Using the S3 extension
@LocalStackTest
@S3Test
@S3Bucket("com-github-awstestkit-bucket")
@S3Objects(
        @S3Object(
                bucketName = "com-github-awstestkit-bucket",
                key = "/path/to/file",
                content = "content"
        )
)
class S3ExtensionTest {
    @Test
    void setupBuckets(@AwsClient S3Client client) {
        assertFalse(client.listBuckets().buckets().isEmpty());
    }
}

7. SecretsManager Extension

The SecretsManager extension allows you to create SecretsManager secrets your tests.

Using the SecretsManager extension
@LocalStackTest
@SecretsManagerTest
@Secrets(
        @Secret(name = "SecretName", value = "SecretValue")
)
public class SecretsManagerExtensionTest {
    @Test
    void setupSecrets(@AwsClient SecretsManagerClient client) {
        assertFalse(client.listSecrets().secretList().isEmpty());
    }
}

8. SNS Extension

The SNS extension allows you to create SNS topics inside your tests.

Using the SNS extension
@LocalStackTest
@SnsTest
@SnsTopic("TopicName")
class SnsExtensionTest {
    @Test
    void setupTopics(@AwsClient SnsClient client) {
        assertFalse(client.listTopics().topics().isEmpty());
    }
}

If you need to use the ARN of a created topic, you can inject it to your test with the @TopicArn annotation.

Resolving a topic ARN
@LocalStackTest
@SnsTest
@SnsTopic("TopicName")
class SnsTopicArnTest {
    @Test
    void setupTopics(@AwsClient SnsClient client, @TopicArn("TopicName") String topicArn) {
        GetTopicAttributesRequest request = GetTopicAttributesRequest.builder()
                .topicArn(topicArn)
                .build();
        assertTrue(client.getTopicAttributes(request).hasAttributes());
    }
}

9. SQS Extension

The SQS extension allows you to create SQS queues inside your tests.

Using the SQS extension
@LocalStackTest
@SqsTest
@SqsQueue("QueueName")
class SqsExtensionTest {
    @Test
    void setupQueues(@AwsClient SqsClient client) {
        assertFalse(client.listQueues().queueUrls().isEmpty());
    }
}

If you need to use the URL of a created queue, you can inject it to your test with the @QueueUrl annotation.

Resolving a queue URL
@LocalStackTest
@SqsTest
@SqsQueue("QueueName")
class SqsQueueUrlTest {
    @Test
    void setupQueues(@AwsClient SqsClient client, @QueueUrl("QueueName") String queueUrl) {
        GetQueueAttributesRequest request = GetQueueAttributesRequest.builder()
                .queueUrl(queueUrl)
                .attributeNames(QueueAttributeName.ALL)
                .build();
        assertFalse(client.getQueueAttributes(request).attributes().isEmpty());
    }
}