DynamoDB Local is a Java application which you can run locally to test your applications against DynamoDB, without the need to authenticate against public AWS endpoints. It’s great for running your unit tests as mocking out something as complex as Dynamo has limited benefit in terms of the completeness of your tests.
DynamoDB local ships as a downloadable jar, a Maven dependency or a Docker container. You’ll find lots of information online about how to fudge the maven dependency into Gradle but I found this hard to understand and debug.
The approach I went with was having Gradle run the Docker container before tests run and then shut it down afterwards. This is fairly simple:
Create Docker Compose File
Create a file called docker-compose.yml in the root of your project (i.e. the same directory as your settings.gradle file):
version: '3.8' services: dynamodb-local: command: "-jar DynamoDBLocal.jar -sharedDb" image: "amazon/dynamodb-local:latest" container_name: dynamodb-local ports: - "8000:8000" working_dir: /home/dynamodblocal
If you already use docker-compose, you can just add the service to the end of your existing file.
This will run a DynamoDB local instance with ephemeral storage (i.e. the data is discarded after the container stops). This is useful as it means you always get a fresh instance for your test runs and, thus, you don’t have previous test runs influencing current ones.
Add to your build.gradle
Add this to your build.gradle:
// Run DynamoDB Local for tests task startDockerLocal(type: Exec) { executable "sh" args "-c", "docker-compose up -d --force-recreate dynamodb-local" } task stopDockerLocal(type: Exec) { executable "sh" args "-c", "docker stop dynamodb-local" } tasks.test.dependsOn("startDockerLocal") tasks.test.finalizedBy("stopDockerLocal")
This defines two tasks. The first runs the container before tests run. The second task stops the container after tests have completed.
Write your Tests
Within your tests, you simply need to create a DynamoDbClient with an endpoint override, like so:
// Create a DynamoDB client which points at the local DynamoDB final DynamoDbClient dynamoDB = DynamoDbClient.builder() .region(Region.of("eu-west-1")) .endpointOverride(URI.create("http://localhost:8000")) .build();
Run your Tests
Now, when you run your tests, you’ll see something like this:
plavin$ ./gradlew clean test > Task :dynamodb-leader-elector:startDockerLocal dynamodb-local Recreating dynamodb-local ... Recreating dynamodb-local ... done > Task :dynamodb-leader-elector:stopDockerLocal dynamodb-local BUILD SUCCESSFUL in 6s 9 actionable tasks: 8 executed, 1 up-to-date
Whilst the tests are running, docker ps will show you the running container:
bash-3.2# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 61a8d8f296ce amazon/dynamodb-local:latest "java -jar DynamoDBL…" 5 seconds ago Up 3 seconds 0.0.0.0:8000->8000/tcp dynamodb-local
Running gradle Inside Docker
If you use docker to build your jars (i.e. run gradlew build from a Dockerfile)… this presents a slight issue. You cannot run Docker containers inside Docker containers (even using docker:dind as your base image) because dind requires –privileged which docker build does not currently support.
I cannot find a good solution to this other than adding -x test to your gradlew build command to stop it running tests:
RUN ./gradlew build -x test