So what does the test look like? If you check out the supplied examples they went for a reusable abstract 'base' test that does the setup which the concrete test classes can then extend. I went for the same approach in both this test as well as previous implementations for other customers.

The only dependencies we need for testing are the TestContainers dependencies and the spring-boot-starter-test dependencies:

AbstractIntegrationTest

So let’s look at our AbstractIntegrationTest first. There are two important parts to this base class:

private static final int DYNAMO_PORT = 8000; @ClassRule public static GenericContainer dynamoDb = new GenericContainer("amazon/dynamodb-local:1.11.119") .withExposedPorts(DYNAMO_PORT);

This bit of code creates a GenericContainer. A GenericContainer can be used to run pretty much any Docker container. But it does not know or understand what is actually running inside. This is where the .withExposedPorts(DYNAMO_PORT) part comes in. DynamoDB listens on port 8000 internally, but that is not what we will be exposing to the outside world. If there would be something else listening on port 8000 already it would fail to start. What .withExposedPorts(DYNAMO_PORT) does is expose the container’s internal port 8000 to a random available port.

The second important bit is overriding the configuration of the system so that it does not try to connect to the endpoint configured in the application.yaml, but connects to the container we just started instead. That’s where our ApplicationContextInitializer instance comes in:

public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { String endpoint = String.format("aws.dynamo.endpoint=http://%s:%s", dynamoDb.getContainerIpAddress(), dynamoDb.getMappedPort(DYNAMO_PORT)); TestPropertyValues.of(endpoint).applyTo(configurableApplicationContext); } }

This bit of code does two things. It formats the container address and container mapped port into a string like aws.dynamo.endpoint=http://localhost:32456 . Then it builds and applies these TestPropertyValues to the current context. This happens before the AmazonDynamoDB bean we defined in the config is created.