Testcontainers (Java)
Official Testcontainers module for MiniStack. One dependency, one MiniStackContainer, zero boilerplate — the container is pulled, started, health-checked, and handed to your test with an endpoint URL, credentials, and region.
Install
Maven:
<dependency> <groupId>org.ministack</groupId> <artifactId>testcontainers-ministack</artifactId> <version>0.1.4</version> <scope>test</scope> </dependency>
Gradle:
testImplementation 'org.ministack:testcontainers-ministack:0.1.4'
The module pulls in Testcontainers core transitively; you don't need to add it separately. You bring your own AWS SDK (v2 recommended).
Quick start
import org.ministack.testcontainers.MiniStackContainer;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.CreateBucketRequest;
try (MiniStackContainer ms = new MiniStackContainer()) {
ms.start();
S3Client s3 = S3Client.builder()
.endpointOverride(URI.create(ms.getEndpoint()))
.region(Region.of(ms.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(ms.getAccessKey(), ms.getSecretKey())))
.forcePathStyle(true)
.build();
s3.createBucket(CreateBucketRequest.builder().bucket("hello").build());
}
API reference
| Member | Returns | Purpose |
|---|---|---|
new MiniStackContainer() | MiniStackContainer | Uses ministackorg/ministack:latest. |
new MiniStackContainer(String tag) | MiniStackContainer | Pins a specific release, e.g. "1.3.14". |
new MiniStackContainer(DockerImageName) | MiniStackContainer | Custom image — useful behind a private registry mirror. |
getEndpoint() | String | e.g. http://127.0.0.1:32789 — feed to every SDK client's endpointOverride. |
getPort() | int | Host-side mapped port for container port 4566. |
getAccessKey() | String | Default "test". Override with .withEnv("AWS_ACCESS_KEY_ID", "111111111111") to scope state to a specific account. |
getSecretKey() | String | Default "test". |
getRegion() | String | Default "us-east-1". |
withRealInfrastructure() | MiniStackContainer | Mounts the Docker socket so RDS/ElastiCache/ECS/EKS can spin up real sidecar containers. |
stop() | void | Stops the container and reaps every sidecar tagged with ministack (works under Docker and Podman). |
The class extends GenericContainer, so everything Testcontainers provides is available: withReuse(true), withEnv(key, val), withLabel(k, v), withCreateContainerCmdModifier(…), and the full wait-strategy API. The built-in wait strategy is an HTTP check on /_ministack/health.
SDK wiring
AWS SDK v2
S3Client s3 = S3Client.builder()
.endpointOverride(URI.create(ms.getEndpoint()))
.region(Region.of(ms.getRegion()))
.credentialsProvider(StaticCredentialsProvider.create(
AwsBasicCredentials.create(ms.getAccessKey(), ms.getSecretKey())))
.forcePathStyle(true) // required for S3
.build();
AWS SDK v1
AmazonS3 s3 = AmazonS3ClientBuilder.standard()
.withEndpointConfiguration(new EndpointConfiguration(ms.getEndpoint(), ms.getRegion()))
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials(ms.getAccessKey(), ms.getSecretKey())))
.withPathStyleAccessEnabled(true)
.build();
Global services (IAM, Route53)
Use Region.AWS_GLOBAL for services that AWS treats as region-less. MiniStack accepts either AWS_GLOBAL or a specific region for these, but AWS_GLOBAL matches AWS SDK expectations:
IamClient iam = IamClient.builder()
.endpointOverride(URI.create(ms.getEndpoint()))
.region(Region.AWS_GLOBAL)
.credentialsProvider(…)
.build();
Real infrastructure
withRealInfrastructure() opts into sidecar mode: RDS CreateDBInstance, ElastiCache CreateCacheCluster, ECS RunTask, and EKS CreateCluster spin up genuine Docker containers on the host daemon. MiniStack talks to them over the host Docker socket, which the module mounts for you.
try (MiniStackContainer ms = new MiniStackContainer().withRealInfrastructure()) {
ms.start();
RdsClient rds = …;
rds.createDBInstance(CreateDBInstanceRequest.builder()
.dbInstanceIdentifier("mydb")
.engine("postgres")
.engineVersion("15")
.masterUsername("admin").masterUserPassword("pw123456")
.dbInstanceClass("db.t3.micro")
.allocatedStorage(20)
.build());
// Await the real postgres container
Awaitility.await().atMost(20, SECONDS)
.until(() -> rds.describeDBInstances(…).dbInstances().get(0)
.dbInstanceStatus().equals("available"));
// Connect with standard JDBC
try (Connection conn = DriverManager.getConnection(
"jdbc:postgresql://localhost:" + port + "/postgres", "admin", "pw123456")) {
// …
}
// Cleanup — required until automatic teardown lands
rds.deleteDBInstance(DeleteDBInstanceRequest.builder()
.dbInstanceIdentifier("mydb")
.skipFinalSnapshot(true)
.build());
}
deleteDBInstance / deleteCacheCluster explicitly before closing the container — the module's stop() reaps leaked containers as a safety net, but explicit cleanup is friendlier to shared dev machines.
Spring Boot
Use @ServiceConnection (Spring Boot 3.1+) to auto-configure an AwsAsyncClient-style bean pointing at the container:
@TestConfiguration(proxyBeanMethods = false)
class MiniStackTestConfig {
@Bean
@ServiceConnection
MiniStackContainer miniStackContainer() {
return new MiniStackContainer("1.3.14");
}
}
Private registry
Air-gapped / registry-proxy environments: set Testcontainers' hub.image.name.prefix in ~/.testcontainers.properties or via TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX. The module forwards the prefix into MINISTACK_IMAGE_PREFIX inside the container, so every nested image (postgres, mysql, redis, k3s, Lambda runtimes) is also pulled through your mirror.
# ~/.testcontainers.properties hub.image.name.prefix=proxy.corp.net/
Reuse across tests
Enable Testcontainers reuse so the container survives between suite runs (dramatic speed-up during local iteration):
// ~/.testcontainers.properties testcontainers.reuse.enable=true // Then in code: new MiniStackContainer().withReuse(true);
When reuse is on, remember to POST /_ministack/reset at the start of your test suite to start from a clean state — otherwise prior runs leak into the new one.
// Before each test class
HttpClient.newHttpClient().send(
HttpRequest.newBuilder(URI.create(ms.getEndpoint() + "/_ministack/reset"))
.POST(HttpRequest.BodyPublishers.noBody()).build(),
HttpResponse.BodyHandlers.discarding());
Gotchas
- Version pinning.
latestchanges under you. Pin to the MiniStack release your CI validated against (the tag string matches a published image). - Docker socket on Podman.
withRealInfrastructure()assumes a Docker-compatible socket. Podman works if you expose the user-scoped socket and setDOCKER_HOSTbefore running tests. - Port mapping is random. Always read
getEndpoint()/getPort()— never hardcode 4566 on the host. - Force path-style for S3. Without
.forcePathStyle(true)the SDK virtual-hosts asbucket.localhostwhich Docker can't resolve.