DynamoDB fake server for integration testing
Solution 1:
I found two possible approaches, DynamoDBEmbedded and Localstack. Regarding the latter, as disclaimed in the website:
LocalStack provides an easy-to-use test/mocking framework for developing Cloud applications. It spins up a testing environment on your local machine that provides the same functionality and APIs as the real AWS cloud environment.
It mocks several aws services including dynamodb. Example:
1) In my pom.xml under dependencies:
<dependency>
<groupId>cloud.localstack</groupId>
<artifactId>localstack-utils</artifactId>
<version>0.1.19</version>
<scope>test</scope>
</dependency>
2) Add these headers to you unit test class:
@RunWith(LocalstackDockerTestRunner.class)
@LocalstackDockerProperties(randomizePorts = true, services = {"dynamodb"})
3) Make sure your application is pointing to localstack url, e.g. dynamo will wait for you at http://localhost:4569
. More information here.
Regarding the former, i.e. DynamoDBEmbedded, we can do the following:
1) In pom.xml under dependencies:
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>1.11.477</version>
<scope>test</scope>
</dependency>
2) Then in our unit test:
private AmazonDynamoDB amazonDynamoDB;
@Before
public void setup() throws Exception {
amazonDynamoDB = DynamoDBEmbedded.create().amazonDynamoDB();//dynamoDB.getAmazonDynamoDB();
amazonDynamoDB.createTable(new CreateTableRequest()
.withTableName(TABLE_NAME)
.withKeySchema(new KeySchemaElement().withAttributeName(ITEM).withKeyType(KeyType.HASH))
.withAttributeDefinitions(
new AttributeDefinition().withAttributeName(ITEM).withAttributeType(ScalarAttributeType.S))
.withProvisionedThroughput(new ProvisionedThroughput(10L, 15L))
);
}
And make sure that our DAOs use this amazonDynamoDB
instance.
Solution 2:
In August 2018 Amazon announced new Docker image with Amazon DynamoDB Local onboard. It does not require downloading and running any JARs as well as adding using third-party OS-specific binaries (I'm talking about sqlite4java
).
It is as simple as starting a Docker container before the tests:
docker run -p 8000:8000 amazon/dynamodb-local
You can do that manually for local development, as described above, or use it in your CI pipeline. Many CI services provide an ability to start additional containers during the pipeline that can provide dependencies for your tests. Here is an example for Gitlab CI/CD:
test:
stage: test
image: openjdk:8-alpine
services:
- name: amazon/dynamodb-local
alias: dynamodb-local
script:
- DYNAMODB_LOCAL_URL=http://dynamodb-local:8000 ./gradlew clean test
Or Bitbucket Pipelines:
definitions:
services:
dynamodb-local:
image: amazon/dynamodb-local
…
step:
name: test
image:
name: openjdk:8-alpine
services:
- dynamodb-local
script:
- DYNAMODB_LOCAL_URL=http://localhost:8000 ./gradlew clean test
After you've started the container you can create a client pointing to it:
private AmazonDynamoDB createAmazonDynamoDB(final DynamoDBLocal configuration) {
return AmazonDynamoDBClientBuilder
.standard()
.withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration(
"http://localhost:8000",
Regions.US_EAST_1.getName()
)
)
.withCredentials(
new AWSStaticCredentialsProvider(
// DynamoDB Local works with any non-null credentials
new BasicAWSCredentials("", "")
)
)
.build();
}
And if you're using JUnit 5, it can be a good idea to use a JUnit 5 extensions for AWS that will inject the client in your tests (yes, I'm doing a self-promotion):
-
Add a dependency on
me.madhead.aws-junit5:dynamo-v1
(dynamo-v2
for AWS Java SDK v2.x client)pom.xml:
<dependency> <groupId>me.madhead.aws-junit5</groupId> <artifactId>dynamo-v1</artifactId> <version>6.0.3</version> <scope>test</scope> </dependency>
build.gradle
dependencies { testImplementation("me.madhead.aws-junit5:dynamo-v1:6.0.3") }
-
Use the extension in your tests:
@ExtendWith(DynamoDB.class) class Test { @AWSClient( endpoint = Endpoint.class ) private AmazonDynamoDB client; @Test void test() { client.listTables(); } }
Read the full user guide, it's really short.