Gradle Logo

Running Amazon DynamoDB Local (Docker) with Gradle

Posted by

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

Leave a Reply

Your email address will not be published.