Examples
Examples of different use cases provided by Testcontainers can be found below:
+ +diff --git a/docs/sourcey/css/extra.css b/docs/sourcey/css/extra.css new file mode 100644 index 00000000000..ff780cb25ea --- /dev/null +++ b/docs/sourcey/css/extra.css @@ -0,0 +1,128 @@ +h1, h2, h3, h4, h5, h6 { + font-family: 'Rubik', sans-serif; +} + +[data-md-color-scheme="testcontainers"] { + --md-primary-fg-color: #00bac2; + --md-accent-fg-color: #361E5B; + --md-typeset-a-color: #0C94AA; + --md-primary-fg-color--dark: #291A3F; + --md-default-fg-color--lightest: #F2F4FE; + --md-footer-fg-color: #361E5B; + --md-footer-fg-color--light: #746C8F; + --md-footer-fg-color--lighter: #C3BEDE; + --md-footer-bg-color: #F7F9FD; + --md-footer-bg-color--dark: #F7F9FD; +} + +.card-grid { + display: grid; + gap: 10px; +} + +.tc-version { + font-size: 1.1em; + text-align: center; + margin: 0; +} + +@media (min-width: 680px) { + .card-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +body .card-grid-item { + display: flex; + align-items: center; + gap: 20px; + border: 1px solid #C3BEDE; + border-radius: 6px; + padding: 16px; + font-weight: 600; + color: #9991B5; + background: #F2F4FE; +} + +body .card-grid-item:hover, +body .card-grid-item:focus { + color: #9991B5; +} + +.card-grid-item[href] { + color: var(--md-primary-fg-color--dark); + background: transparent; +} + +.card-grid-item[href]:hover, +.card-grid-item[href]:focus { + background: #F2F4FE; + color: var(--md-primary-fg-color--dark); +} + +.community-callout-wrapper { + padding: 30px 10px 0 10px; +} + +.community-callout { + color: #F2F4FE; + background: linear-gradient(10.88deg, rgba(102, 56, 242, 0.4) 9.56%, #6638F2 100%), #291A3F; + box-shadow: 0px 20px 45px rgba(#9991B5, 0.75); + border-radius: 10px; + padding: 20px; +} + +.community-callout h2 { + font-size: 1.15em; + margin: 0 0 20px 0; + color: #F2F4FE; + text-align: center; +} + +.community-callout ul { + list-style: none; + padding: 0; + display: flex; + justify-content: space-between; + gap: 10px; + margin-top: 20px; + margin-bottom: 0; +} + +.community-callout a { + transition: opacity 0.2s ease; +} + +.community-callout a:hover { + opacity: 0.5; +} + +.community-callout a img { + height: 1.75em; + width: auto; + aspect-ratio: 1; +} + +@media (min-width: 1220px) { + .community-callout-wrapper { + padding: 40px 0 0; + } + + .community-callout h2 { + font-size: 1.25em; + } + + .community-callout a img { + height: 2em; + } +} + +@media (min-width: 1600px) { + .community-callout h2 { + font-size: 1.15em; + } + + .community-callout a img { + height: 1.75em; + } +} \ No newline at end of file diff --git a/docs/sourcey/css/tc-header.css b/docs/sourcey/css/tc-header.css new file mode 100644 index 00000000000..de78d636e9c --- /dev/null +++ b/docs/sourcey/css/tc-header.css @@ -0,0 +1,389 @@ + +:root { + --color-catskill: #F2F4FE; + --color-catskill-45: rgba(242, 244, 254, 0.45); + --color-mist: #E7EAFB; + --color-fog: #C3C7E6; + --color-smoke: #9991B5; + --color-smoke-75: rgba(153, 145, 181, 0.75); + --color-storm: #746C8F; + --color-topaz: #00BAC2; + --color-pacific: #17A6B2; + --color-teal: #027F9E; + --color-eggplant: #291A3F; + --color-plum: #361E5B; + +} + +#site-header { + color: var(--color-storm); + background: #fff; + font-family: 'Rubik', Arial, Helvetica, sans-serif; + font-size: 12px; + line-height: 1.5; + position: relative; + width: 100%; + z-index: 4; + display: flex; + align-items: center; + justify-content: space-between; + gap: 20px; + padding: 20px; +} + +body.tc-header-active #site-header { + z-index: 5; +} + +#site-header .brand { + display: flex; + justify-content: space-between; + gap: 20px; + width: 100%; +} + +#site-header .logo { + display: flex; +} + +#site-header .logo img, +#site-header .logo svg { + height: 30px; + width: auto; + max-width: 100%; +} + +#site-header #mobile-menu-toggle { + background: none; + border: none; + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + color: var(--color-eggplant); + padding: 0; + margin: 0; + font-weight: 500; +} + +body.mobile-menu #site-header #mobile-menu-toggle { + color: var(--color-topaz); +} + +#site-header ul { + list-style: none; + padding: 0; + margin: 0; +} + +#site-header nav { + display: none; +} + +#site-header .menu-item { + display: flex; +} + +#site-header .menu-item button, +#site-header .menu-item a { + min-height: 30px; + display: flex; + gap: 6px; + align-items: center; + border: none; + background: none; + cursor: pointer; + padding: 0; + font-weight: 500; + color: var(--color-eggplant); + text-decoration: none; + font-size: 14px; + transition: color 0.2s ease; + white-space: nowrap; +} + +#site-header .menu-item .badge { + color: white; + font-size: 10px; + padding: 2px 6px; + background-color: #0FD5C6; // somehow $topaz is too dark for me. +text-align: center; + text-decoration: none; + display: inline-block; + border-radius: 6px; + &:hover { + + } +} + +#site-header .menu-item button:hover, +#site-header .menu-item a:hover { + color: var(--color-topaz); +} + +#site-header .menu-item button .icon-external, +#site-header .menu-item a .icon-externa { + margin-left: auto; + opacity: .3; + flex-shrink: 0; +} + +#site-header .menu-item button .icon-caret, +#site-header .menu-item a .icon-caret { + opacity: .3; + height: 8px; +} + +#site-header .menu-item button .icon-slack, +#site-header .menu-item a .icon-slack, +#site-header .menu-item button .icon-github, +#site-header .menu-item a .icon-github { + height: 18px; +} + +#site-header .menu-item .menu-dropdown { + flex-direction: column; +} + +body #site-header .menu-item .menu-dropdown { + display: none; +} + +#site-header .menu-item.has-children.active .menu-dropdown { + display: flex; + z-index: 10; +} + +#site-header .menu-dropdown-item + .menu-dropdown-item { + border-top: 1px solid var(--color-mist); +} + +#site-header .menu-dropdown-item a { + display: flex; + gap: 10px; + align-items: center; + padding: 10px 20px; + font-weight: 500; + color: var(--color-eggplant); + text-decoration: none; + transition: + color 0.2s ease, + background 0.2s ease; +} + +#site-header .menu-dropdown-item a .icon-external { + margin-left: auto; + color: var(--color-fog); + flex-shrink: 0; + opacity: 1; +} + +#site-header .menu-dropdown-item a:hover { + background-color: var(--color-catskill-45); +} + +#site-header .menu-dropdown-item a:hover .icon-external { + color: var(--color-topaz); +} + +#site-header .menu-dropdown-item a img { + height: 24px; +} + +.md-header { + background-color: var(--color-catskill); + color: var(--color-eggplant); +} + +.md-header.md-header--shadow { + box-shadow: none; +} + +.md-header__inner.md-grid { + max-width: 100%; + padding: 1.5px 20px; +} + +[dir=ltr] .md-header__title { + margin: 0; +} + +.md-header__topic:first-child { + font-size: 16px; + font-weight: 500; + font-family: 'Rubik', Arial, Helvetica, sans-serif; +} + +.md-header__title.md-header__title--active .md-header__topic, +.md-header__title[data-md-state=active] .md-header__topic { + opacity: 1; + pointer-events: all; + transform: translateX(0); + transition: none; + z-index: 0; +} + +.md-header__topic a { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + transition: color .2s ease; +} + +.md-header__topic a:hover { + color: var(--color-topaz); +} + +div.md-header__source { + width: auto; +} + +div.md-source__repository { + max-width: 100%; +} + +.md-main { + padding: 0 12px; +} + +@media screen and (min-width: 60em) { + form.md-search__form { + background-color: #FBFBFF; + color: var(--color-storm); + } + + form.md-search__form:hover { + background-color: #fff; + } + + .md-search__input + .md-search__icon { + color: var(--color-plum); + } + + .md-search__input::placeholder { + color: var(--color-smoke); + } +} + +@media (min-width: 500px) { + #site-header { + font-size: 16px; + padding: 20px 40px; + } + #site-header .logo img, + #site-header .logo svg { + height: 48px; + } + + #site-header .menu-item button .icon-caret, + #site-header .menu-item a .icon-caret { + height: 10px; + } + + #site-header .menu-item button .icon-slack, + #site-header .menu-item a .icon-slack, + #site-header .menu-item button .icon-github, + #site-header .menu-item a .icon-github { + height: 24px; + } + + .md-header__inner.md-grid { + padding: 5px 40px; + } + + .md-main { + padding: 0 32px; + } +} + +@media (min-width: 1024px) { + #site-header #mobile-menu-toggle { + display: none; + } + + #site-header nav { + display: block; + } + + #site-header .menu { + display: flex; + justify-content: center; + gap: 30px; + } + + #site-header .menu-item { + align-items: center; + position: relative; + } + + #site-header .menu-item button, + #site-header .menu-item a { + min-height: 48px; + gap: 8px; + font-size: 16px; + } + + #site-header .menu-item .menu-dropdown { + position: absolute; + top: 100%; + right: -8px; + border: 1px solid var(--color-mist); + border-radius: 6px; + background: #fff; + box-shadow: 0px 30px 35px var(--color-smoke-75); + min-width: 200px; + } +} + + +@media (max-width: 1023px) { + #site-header { + flex-direction: column; + } + + body.mobile-tc-header-active #site-header { + z-index: 5; + } + + body.mobile-menu #site-header nav { + display: flex; + } + + #site-header nav { + position: absolute; + top: calc(100% - 5px); + width: calc(100% - 80px); + flex-direction: column; + border: 1px solid var(--color-mist); + border-radius: 6px; + background: #fff; + box-shadow: 0px 30px 35px var(--color-smoke-75); + min-width: 200px; + } + + #site-header .menu-item { + flex-direction: column; + } + #site-header .menu-item + .menu-item { + border-top: 1px solid var(--color-mist); + } + + #site-header .menu-item button, + #site-header .menu-item a { + padding: 10px 20px; + } + + #site-header .menu-item.has-children.active .menu-dropdown { + border-top: 1px solid var(--color-mist); + } + + #site-header .menu-dropdown-item a { + padding: 10px 20px 10px 30px; + } +} + +@media (max-width: 499px) { + #site-header nav { + width: calc(100% - 40px); + } +} diff --git a/docs/sourcey/examples.html b/docs/sourcey/examples.html new file mode 100644 index 00000000000..f646e6e923a --- /dev/null +++ b/docs/sourcey/examples.html @@ -0,0 +1,41 @@ + +
Examples of different use cases provided by Testcontainers can be found below:
+ +withLabel:To add a custom label to the container, use withLabel:
Adding a single label inside_block:single_label
+ + +Additionally, multiple labels may be applied together from a map:
+ +Adding multiple labels inside_block:multiple_labels
+ + +By default, the container image is retrieved from the local Docker images cache. +This works well when running against a specific version, but for images with a static tag (i.e. 'latest') this may lead to a newer version not being pulled.
+It is possible to specify an Image Pull Policy to determine at runtime whether an image should be pulled or not:
+ +Setting image pull policy inside_block:built_in_image_pull_policy
+ + +... or providing a function:
+ +Custom image pull policy inside_block:custom_image_pull_policy
+ + +You can also configure Testcontainers to use your custom implementation by using pull.policy
src/test/resources/testcontainers.properties
pull.policy=com.mycompany.testcontainers.ExampleImagePullPolicyYou can also use the provided implementation to always pull images
+src/test/resources/testcontainers.properties
pull.policy=org.testcontainers.images.AlwaysPullPolicyPlease see the documentation on configuration mechanisms for more information.
+It is possible to use the docker-java API directly to customize containers before creation. This is useful if there is a need to use advanced Docker features that are not exposed by the Testcontainers API. Any customizations you make using withCreateContainerCmdModifier will be applied on top of the container definition that Testcontainers creates, but before it is created.
For example, this can be used to change the container hostname:
+ +Using modifier to change hostname inside_block:hostname
+ + +... or modify container memory (see this if it does not appear to work):
+ +Using modifier to change memory limits inside_block:memory
+ + +++Note +It is recommended to use this sparingly, and follow changes to the
+docker-javaAPI if you choose to use this. +It is typically quite stable, though.
For what is possible, consult the docker-java CreateContainerCmd source code.
Testcontainers provides a CreateContainerCmdModifier to customize docker-java CreateContainerCmd
+via Service Provider Interface (SPI) mechanism.
CreateContainerCmd example implementation
+ + +The previous implementation should be registered in META-INF/services/org.testcontainers.core.CreateContainerCmdModifier file.
++Warning +
+CreateContainerCmdModifierimplementation will apply to all containers created by Testcontainers.
Usually, containers are started sequentially when more than one container is used.
+Using Startables.deepStart(container1, container2, ...).join() will start all containers in parallel.
+This can be advantageous to reduce the impact of the container startup overhead.
withCommand. For example:By default the container will execute whatever command is specified in the image's Dockerfile. To override this, and specify a different command, use withCommand. For example:
Specifying a startup command inside_block:startupCommand
+ + +Your test can execute a command inside a running container, similar to a docker exec call:
Executing a command inside a running container inside_block:standaloneExec
+ + +This can be useful for software that has a command line administration tool. You can also get the output (stdout/stderr) and exit code from the command - for example:
+ +Executing a command inside a running container and reading the result inside_block:execReadingStdout
+ + +To add environment variables to the container, use withEnv:
new GenericContainer(...)
+ .withEnv("API_TOKEN", "foo")You can override some default properties if your environment requires that.
+The configuration will be loaded from multiple locations. Properties are considered in the following order:
+.testcontainers.properties in user's home folder. Example locations:/home/myuser/.testcontainers.propertiesC:/Users/myuser/.testcontainers.properties/Users/myuser/.testcontainers.propertiestestcontainers.properties on the classpath.Note that when using environment variables, configuration property names should be set in upper
+case with underscore separators, preceded by TESTCONTAINERS_ - e.g. checks.disable becomes
+TESTCONTAINERS_CHECKS_DISABLE.
The classpath testcontainers.properties file may exist within the local codebase (e.g. within the src/test/resources directory) or within library dependencies that you may have.
+Any such configuration files will have their contents merged.
+If any keys conflict, the value will be taken on the basis of the first value found in:
file:), then++checks.disable = [true|false]
+
Before running any containers Testcontainers will perform a set of startup checks to ensure that your environment is configured correctly. Usually they look like this:
+ ℹ︎ Checking the system...
+ ✔ Docker version should be at least 1.6.0
+ ✔ File should be mountable
+ ✔ A port exposed by a docker container should be accessibleIt takes a couple of seconds, but if you want to speed up your tests, you can disable the checks once you have everything configured. Add checks.disable=true to your $HOME/.testcontainers.properties to completely disable them.
++Note +This approach is discouraged and deprecated, but is documented for completeness. +Overriding individual image names via configuration may be removed in 2021. +See Image Name Substitution for other strategies for substituting image names to pull from other registries.
+
Testcontainers uses public Docker images to perform different actions like startup checks, VNC recording and others.
+Some companies disallow the usage of Docker Hub, but you can override *.image properties with your own images from your private registry to workaround that.
++ryuk.container.image = testcontainers/ryuk:0.3.3 +Performs fail-safe cleanup of containers, and always required (unless Ryuk is disabled)
+
++tinyimage.container.image = alpine:3.17
+
Used to check whether images can be pulled at startup, and always required (unless startup checks are disabled)
++sshd.container.image = testcontainers/sshd:1.4.0
+
Required if exposing host ports to containers
++vncrecorder.container.image = testcontainers/vnc-recorder:1.3.0 +Used by VNC recorder in Testcontainers' Selenium integration
+
++socat.container.image = alpine/socat
+
compose.container.image = docker/compose:1.8.0
Required if using Docker Compose
++kafka.container.image = confluentinc/cp-kafka
+
Used by KafkaContainer
++localstack.container.image = localstack/localstack
+
Used by LocalStack
++pulsar.container.image = apachepulsar/pulsar:2.2.0
+
Used by Apache Pulsar
++ryuk.container.image = testcontainers/ryuk:0.3.3 +The resource reaper is responsible for container removal and automatic cleanup of dead containers at JVM shutdown
+
++ryuk.container.privileged = true +In some environments ryuk must be started in privileged mode to work properly (--privileged flag)
+
Ryuk must be started as a privileged container.
If your environment already implements automatic cleanup of containers after the execution,
+but does not allow starting privileged containers, you can turn off the Ryuk container by setting
+TESTCONTAINERS_RYUK_DISABLED environment variable to true.
!!!tip
+ Note that Testcontainers will continue doing the cleanup at JVM's shutdown, unless you kill -9 your JVM process.
++pull.timeout = 120 +By default Testcontainers will timeout if pull takes more than this duration (in seconds)
+
++pull.pause.timeout = 30 +By default Testcontainers will abort the pull of an image if the pull appears stalled (no data transferred) for longer than this duration (in seconds).
+
++client.ping.timeout = 10 +Specifies for how long Testcontainers will try to connect to the Docker client to obtain valid info about the client before giving up and trying next strategy, if applicable (in seconds).
+
Testcontainers will attempt to detect the Docker environment and configure everything to work automatically.
+However, sometimes customization is required. Testcontainers will respect the following environment variables:
+++DOCKER_HOST = unix:///var/run/docker.sock
+
See Docker environment variablesTESTCONTAINERS_DOCKER_SOCKET_OVERRIDE
+
Path to Docker's socket. Used by Ryuk, Docker Compose, and a few other containers that need to perform Docker actions.
Example:/var/run/docker-alt.sockTESTCONTAINERS_HOST_OVERRIDE
+
Docker's host on which ports are exposed.
Example:docker.svc.local
For advanced users, the Docker host connection can be configured via configuration in ~/.testcontainers.properties.
+Note that these settings require use of the EnvironmentAndSystemPropertyClientProviderStrategy. The example below
+illustrates usage:
docker.client.strategy=org.testcontainers.dockerclient.EnvironmentAndSystemPropertyClientProviderStrategy
+docker.host=tcp\://my.docker.host\:1234 # Equivalent to the DOCKER_HOST environment variable. Colons should be escaped.
+docker.tls.verify=1 # Equivalent to the DOCKER_TLS_VERIFY environment variable
+docker.cert.path=/some/path # Equivalent to the DOCKER_CERT_PATH environment variableIn addition, you can deactivate this behaviour by specifying:
+dockerconfig.source=autoIgnoringUserProperties # 'auto' by defaultIt is possible to capture container output using:
+getLogs() method, which simply returns a String snapshot of a container's entire log outputfollowOutput() method. This method accepts a Consumer and (optionally)
+a varargs list stating which of STDOUT, STDERR, or both, should be followed. If not specified, both will be followed.At present, container output will always begin from the time of container creation.
+getLogs() is the simplest mechanism for accessing container logs, and can be used as follows:
Accessing all output (stdout and stderr) inside_block:docsGetAllLogs
+ + + +Accessing just stdout inside_block:docsGetStdOut
+ + + +Accessing just stderr inside_block:docsGetStdErr
+ + +Testcontainers includes some out-of-the-box Consumer implementations that can be used with the streaming followOutput() model; examples follow.
Given an existing SLF4J logger instance named LOGGER:
+Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER);
+container.followOutput(logConsumer);By default both standard out and standard error will both be emitted at INFO level. +Standard error may be emitted at ERROR level, if desired:
+Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withSeparateOutputStreams();The Mapped Diagnostic Context (MDC) for emitted messages may be configured using the withMdc(...) option:
Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withMdc("key", "value");or using an existing map of key-value pairs:
+Slf4jLogConsumer logConsumer = new Slf4jLogConsumer(LOGGER).withMdc(map);To stream logs live or customize the decoding, ToStringConsumer may be used:
ToStringConsumer toStringConsumer = new ToStringConsumer();
+container.followOutput(toStringConsumer, OutputType.STDOUT);
+
+String utf8String = toStringConsumer.toUtf8String();
+
+// Or if the container output is not UTF-8
+String otherString = toStringConsumer.toString(CharSet.forName("ISO-8859-1"));WaitingConsumer will block until a frame of container output (usually a line) matches a provided predicate.
A timeout may be specified, as shown in this example.
+WaitingConsumer consumer = new WaitingConsumer();
+
+container.followOutput(consumer, STDOUT);
+
+consumer.waitUntil(frame ->
+ frame.getUtf8String().contains("STARTED"), 30, TimeUnit.SECONDS);Additionally, as the Java 8 Consumer functional interface is used, Consumers may be composed together. This is +useful, for example, to capture all the container output but only when a matching string has been found. e.g.:
+WaitingConsumer waitingConsumer = new WaitingConsumer();
+ToStringConsumer toStringConsumer = new ToStringConsumer();
+
+Consumer<OutputFrame> composedConsumer = toStringConsumer.andThen(waitingConsumer);
+container.followOutput(composedConsumer);
+
+waitingConsumer.waitUntil(frame ->
+ frame.getUtf8String().contains("STARTED"), 30, TimeUnit.SECONDS);
+
+String utf8String = toStringConsumer.toUtf8String();Testcontainers' generic container support offers the most flexibility, and makes it easy to use virtually any container +images as temporary test dependencies. For example, if you might use it to test interactions with:
+With a generic container, you set the container image using a parameter to the rule constructor, e.g.:
+new GenericContainer(DockerImageName.parse("jboss/wildfly:9.0.1.Final"))Many Container classes in Testcontainers have historically supported:
+new GenericContainer() and new ElasticsearchContainer(). With these constructors, Testcontainers has traditionally used a default image name (including a fixed image tag/version). This has caused a conflict between the need to keep the defaults sane (i.e. up to date) and the need to avoid silently upgrading these dependencies along with new versions of Testcontainers. Since v1.15.0, both of these constructor types have been deprecated, for the reasons given above.
+Instead, it is highly recommended that all containers be constructed using a constructor that accepts a DockerImageName object.
+The DockerImageName class is an unambiguous reference to a docker image.
It is suggested that developers treat DockerImageNames as you would any other potentially-constant value - consider defining a constant in your test codebase that matches the production version of the dependency you are using.
A generic container rule can be used with any public docker image; for example:
+ +Creating a Redis container inside_block:container
+ + +The container, as @Container, will be started before any tests in the class run, and will be destroyed after all
+tests have run.
In situations where there is no pre-existing Docker image, Testcontainers can create a new temporary image on-the-fly +from a Dockerfile. For example, when the component under test is the Docker image itself, or when an existing base +image needs to be customized for specific test(s).
+Simply pass a configured instance of ImageFromDockerfile as a constructor parameter to GenericContainer.
+Testcontainers will docker build a temporary container image, and will use it when creating the container.
ImageFromDockerfile accepts arbitrary files, strings or classpath resources to be used as files in the build context.
+At least one of these needs to be a Dockerfile.
GenericContainer container = new GenericContainer(
+ new ImageFromDockerfile()
+ .withFileFromString("folder/someFile.txt", "hello")
+ .withFileFromClasspath("test.txt", "mappable-resource/test-resource.txt")
+ .withFileFromClasspath("Dockerfile", "mappable-dockerfile/Dockerfile"))The following methods may be used to provide the Dockerfile and any other required build context files:
withFileFromString(buildContextPath, content)withFileFromClasspath(buildContextPath, classpathPath)withFileFromPath(buildContextPath, filesystemPath)withFileFromFile(buildContextPath, filesystemFile)++Info +In older versions of Testcontainers (before 1.3.0) it was necessary to explicitly declare each file that needed to +be present in the Docker build context. +This can be replaced with the following syntax:
+ +Passing an entire directory of files to the Dockerfile build context inside_block:docsShowRecursiveFileInclusion
+ + +Where
+RESOURCE_PATHis the path to a directory containing aDockerfileand any files that it needs to refer to. +Doing this is equivalent todocker build RESOURCE_PATHon the command line.To mimic
+docker build .,RESOURCE_PATHwould simply be set to.as well.
If a static Dockerfile is not sufficient (e.g. your test needs to cover many variations that are best generated +programmatically), there is a DSL available to allow Dockerfiles to be defined in code. e.g.:
+new GenericContainer(
+ new ImageFromDockerfile()
+ .withDockerfileFromBuilder(builder ->
+ builder
+ .from("alpine:3.17")
+ .run("apk add --update nginx")
+ .cmd("nginx", "-g", "daemon off;")
+ .build()))
+ .withExposedPorts(80);See ParameterizedDockerfileContainerTest for a very basic example of using this in conjunction with JUnit
+parameterized testing.
Temporary container images will be automatically removed when the test JVM shuts down. If this is not desired and
+the image should be retained between tests, pass a stable image name and false flag to the ImageFromDockerfile
+constructor.
Retaining the image between tests will use Docker's image cache to accelerate subsequent test runs.
+By default the no-args constructor will use an image name of the form testcontainers/ + random string:
public ImageFromDockerfile()public ImageFromDockerfile(String dockerImageName)public ImageFromDockerfile(String dockerImageName, boolean deleteOnExit)Normally Docker will automatically build an image from any /Dockerfile that it finds in the root of the build context.
+To override this behaviour, use .withDockerfilePath("./Name-Of-Other-Dockerfile").
Build Args may be used to allow lightweight parameterization.
+To specify build args, use .withBuildArg("varname", "value") or provide a Map of args using .withBuildArgs(map).
Files can be copied into the container before startup, or can be copied from the container after the container has started.
+++Note +This is the recommended approach for portability cross-docker environments.
+
Copying files using MountableFile inside_block:copyToContainer
+ + +Using Transferable, file content will be placed in the specified location.
Copying files using Transferable inside_block:transferableFile
+ + +Setting file mode is also possible.
+ +Copying files using Transferable with file mode inside_block:transferableWithFileMode
+ + +Copying files from a container inside_block:copyFileFromContainer
+ +Testcontainers supports automatic substitution of Docker image names.
+This allows replacement of an image name specified in test code with an alternative name - for example, to replace the +name of a Docker Hub image dependency with an alternative hosted on a private image registry.
+This is advisable to avoid Docker Hub rate limiting, and some companies will prefer this for policy reasons.
+This page describes four approaches for image name substitution:
+It is assumed that you have already set up a private registry hosting all the Docker images your build requires.
+Consider this if:
+This approach simply entails modifying test code manually, e.g. changing:
+For example, you may have a test that uses the mysql container image from Docker Hub:
Direct Docker Hub image name inside_block:directDockerHubReference
+ + +to:
+ +Private registry image name inside_block:hardcodedMirror
+ + + + + + +Testcontainers can be configured to modify Docker Hub image names on the fly to apply a prefix string.
+Consider this if:
+registry.mycompany.com/mirror/mysql:8.0.36 can be derived from the original Docker Hub image name (mysql:8.0.36) with a consistent prefix string: registry.mycompany.com/mirror/In this case, image name references in code are unchanged. +i.e. you would leave as-is:
+ +Unchanged direct Docker Hub image name inside_block:directDockerHubReference
+ + +You can then configure Testcontainers to apply the prefix registry.mycompany.com/mirror/ to every image that it tries to pull from Docker Hub.
+This can be done in one of two ways:
TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=registry.mycompany.com/mirror/hub.image.name.prefix in either:~/.testcontainers.properties file in your user home directory, ortestcontainers.properties on the classpathTestcontainers will automatically apply the prefix to every image that it pulls from Docker Hub - please verify that all the required images exist in your registry.
+Testcontainers will not apply the prefix to:
+docker.io or registry.hub.docker.com host part)Consider this if:
+In this case, image name references in code are unchanged. +i.e. you would leave as-is:
+ +Unchanged direct Docker Hub image name inside_block:directDockerHubReference
+ + +You can implement a custom image name substitutor by:
+org.testcontainers.utility.ImageNameSubstitutorThe following is an example image substitutor implementation:
+ +Example Image Substitutor block:ExampleImageNameSubstitutor
+ + +Testcontainers can be configured to find it at runtime via configuration.
+To do this, create or modify a file on the classpath named testcontainers.properties.
For example:
+src/test/resources/testcontainers.properties
image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutorNote that it is also possible to provide this same configuration property:
+testcontainers.properties file at the root of a library JAR file (useful if you wish to distribute a drop-in image substitutor JAR within an organization) ~/.testcontainers.properties; note the leading .)TESTCONTAINERS_IMAGE_SUBSTITUTOR=com.mycompany.testcontainers.ExampleImageNameSubstitutor).Please see the documentation on configuration mechanisms for more information.
+Also, you can use the ServiceLoader mechanism to provide the fully qualified class name of the ImageNameSubstitutor implementation:
src/test/resources/META-INF/services/org.testcontainers.utility.ImageNameSubstitutor
com.mycompany.testcontainers.ExampleImageNameSubstitutor++Note +This approach is discouraged and deprecated, but is documented for completeness. +Please consider one of the other approaches outlined in this page instead. +Overriding individual image names via configuration may be removed in the future.
+
Consider this if:
+In this case, image name references in code are left unchanged. +i.e. you would leave as-is:
+ +Unchanged direct Docker Hub image name inside_block:directDockerHubReference
+ + +You can force Testcontainers to substitute in a different image using a configuration file, which allows some (but not all) container names to be substituted.
+It is common to want to connect to a container from your test process, running on the test 'host' machine. +For example, you may be testing a class that needs to connect to a backend or data store container.
+Generally, each required port needs to be explicitly exposed. For example, we can specify one or more ports as follows:
+ +Exposing ports inside_block:rule
+ + +Note that this exposed port number is from the perspective of the container.
+From the host's perspective Testcontainers actually exposes this on a random free port. +This is by design, to avoid port collisions that may arise with locally running software or in between parallel test runs.
+Because there is this layer of indirection, it is necessary to ask Testcontainers for the actual mapped port at runtime.
+This can be done using the getMappedPort method, which takes the original (container) port as an argument:
Retrieving actual ports at runtime inside_block:fetchPortsByNumber
+ + +++Warning +Because the randomised port mapping happens during container startup, the container must be running at the time
+getMappedPortis called. +You may need to ensure that the startup order of components in your tests caters for this.
There is also a getFirstMappedPort method for convenience, for the fairly common scenario of a container that only exposes one port:
Retrieving the first mapped port inside_block:fetchFirstMappedPort
+ + +When running with a local Docker daemon, exposed ports will usually be reachable on localhost.
+However, in some CI environments they may instead be reachable on a different host.
As such, Testcontainers provides a convenience method to obtain an address on which the container should be reachable from the host machine.
+ +Getting the container host inside_block:getHostOnly
+ + +It is normally advisable to use getHost and getMappedPort together when constructing addresses - for example:
Getting the container host and mapped port inside_block:getHostAndMappedPort
+ + +++Tip +
+getHost()is a replacement forgetContainerIpAddress()and returns the same result. +getContainerIpAddress()is believed to be confusingly named, and will eventually be deprecated.
In some cases it is necessary to make a network connection from a container to a socket that is listening on the host machine. +Natively, Docker has limited support for this model across platforms. +Testcontainers, however, makes this possible.
+In this example, assume that localServerPort is a port on our test host machine where a server (e.g. a web application) is running.
We need to tell Testcontainers to prepare to expose this port to containers:
+ +Exposing the host port inside_block:exposePort
+ + +++Warning +Note that the above command should be invoked before containers are started, but after the server on the host was started.
+
Alternatively, usecontainer.withAccessToHost(true)to force the host access mechanism (you still need to callexposeHostPortsto make the port available).
Having done so, we can now access this port from any containers that are launched.
+From a container's perspective, the hostname will be host.testcontainers.internal and the port will be the same value as localServerPort.
For example, here we construct an HTTP URL for our local web application and tell a Selenium container to get a page from it:
+ +Accessing the exposed host port from a container inside_block:useHostExposedPort
+ + + +Docker provides the ability for you to create custom networks and place containers on one or more networks. Then, communication can occur between networked containers without the need of exposing ports through the host. With Testcontainers, you can do this as well.
+++ +Warning +Note that Testcontainers currently only allows a container to be on a single network.
+
Creating custom networks inside_block:useCustomNetwork
+ +start() method, do not call stop() method directly or indirectly via try-with-resources or JUnit integration, and enable it manually through an opt-in mechanism per environment. To reuse a container, the container configuration must be the same.++Warning +Reusable Containers is still an experimental feature and the behavior can change. +Those containers won't stop after all tests are finished.
+
The Reusable feature keeps the containers running and next executions with the same container configuration
+will reuse it. To use it, start the container manually by calling start() method, do not call stop() method
+directly or indirectly via try-with-resources or JUnit integration, and enable it manually through an
+opt-in mechanism per environment. To reuse a container, the container configuration must be the same.
++Note +Reusable containers are not suited for CI usage and as an experimental feature +not all Testcontainers features are fully working (e.g., resource cleanup +or networking).
+
Reusable Containers TESTCONTAINERS_REUSE_ENABLE=true ~/.testcontainers.properties, by adding testcontainers.reuse.enable=true withReuse(true)GenericContainer container = new GenericContainer("redis:6-alpine")
+ .withExposedPorts(6379)
+ .withReuse(true)container.start()If using the Testcontainers JDBC URL support
+the URL must follow the pattern of jdbc:tc:mysql:8.0.36:///databasename?TC_REUSABLE=true.
+TC_REUSABLE=true is set as a parameter of the JDBC URL.
++Wait strategies vs Startup strategies
+Wait strategy: is the container in a state that is useful for testing. This is generally approximated as 'can we talk to this container over the network'. However, there are quite a few variations and nuances.
+Startup strategy: did a container reach the desired running state. Almost always this just means 'wait until the container is running' - for a daemon process in a container this is the goal. Sometimes we need to wait until the container reaches a running state and then exits - this is the 'one shot startup' strategy, only used for cases where we need to run a one off command in a container but not a daemon.
+
Ordinarily Testcontainers will wait for up to 60 seconds for the container's first mapped network port to start listening.
+This simple measure provides a basic check whether a container is ready for use.
+ +Waiting for the first exposed port to start listening inside_block:waitForNetworkListening
+ + +If the default 60s timeout is not sufficient, it can be altered with the withStartupTimeout() method.
If waiting for a listening TCP port is not sufficient to establish whether the container is ready, you can use the
+waitingFor() method with other [WaitStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html) implementations as shown below.
You can choose to wait for an HTTP(S) endpoint to return a particular status code.
+inside_block:waitForSimpleHttp
+ + +Variations on the HTTP wait strategy are supported, including:
+inside_block:waitForHttpWithMultipleStatusCodes
+ + +Waiting for a status code that matches a predicate inside_block:waitForHttpWithStatusCodePredicate
+ + +inside_block:waitForHttpWithTls
+ + +If the used image supports Docker's Healthcheck feature, you can directly leverage the healthy state of the container as your wait condition:
In some situations a container's log output is a simple way to determine if it is ready or not. +For example, we can wait for a `Ready' message in the container's logs as follows:
+ + + + +For further options, check out the [Wait](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/Wait.html) convenience class, or the various subclasses of [WaitStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html).
If none of these options meet your requirements, you can create your own subclass of
+[AbstractWaitStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/AbstractWaitStrategy.html) with an
+appropriate wait mechanism in waitUntilReady().
+The GenericContainer.waitingFor() method accepts any valid [WaitStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/wait/strategy/WaitStrategy.html).
Ordinarily Testcontainers will check that the container has reached the running state and has not exited. +In order to do that inspect is executed against the container and state parameter is extracted.
+All logic is implemented in [StartupCheckStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/StartupCheckStrategy.html) child classes.
This is the strategy used by default. Testcontainers just checks if container is running.
+Implemented in [IsRunningStartupCheckStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers/startupcheck/IsRunningStartupCheckStrategy.html) class.
This strategy is intended for use with containers that only run briefly and exit of their own accord. As such, success is deemed to be when +the container has stopped with exit code 0.
+ +Using one shot startup strategy inside_block:withOneShotStrategy
+ + +Variant of one shot strategy that does not impose a timeout. Intended for situation such as when a long running task forms part of + container startup.
+It has to be assumed that the container will stop of its own accord, either with a success or failure exit code.
+ +Using indefinite one shot startup strategy inside_block:withIndefiniteOneShotStrategy
+ + +Checks that the container is running and has been running for a defined minimum period of time.
+ +Using minimum duration strategy inside_block:withMinimumDurationStrategy
+ + +If none of these options meet your requirements, you can create your own subclass of
+[StartupCheckStrategy](http://static.javadoc.io/org.testcontainers/testcontainers/{{ latest_version }}/org/testcontainers/containers
+/startupcheck/StartupCheckStrategy.html) with an appropriate startup check mechanism in waitUntilStartupSuccessful().
+Or you can leave it as is and just implement the checkStartupState(DockerClient dockerClient, String containerId) if you still want to check state
+ periodically.
Sometimes, a container relies on another container to be ready before it should start itself. An example of this might be a database that needs to be started before your application container can link to it. You can tell a container that it depends on another container by using the dependsOn method:
Depending on another container inside_block:dependsOn
+ +Not using Java? Here are other supported languages!
+ + +Testcontainers for Java is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
+Testcontainers make the following kinds of tests easier:
+GenericContainer as a base.Testcontainers is distributed as separate JARs with a common version number:
+For the core library, the latest Maven/Gradle dependency is as follows:
+Gradle
+testImplementation "org.testcontainers:testcontainers:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>You can also check the latest version available on Maven Central.
+To avoid specifying the version of each dependency, you can use a BOM or Bill Of Materials.
Using Maven you can add the following to dependencyManagement section in your pom.xml:
+Maven
<dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-bom</artifactId>
+ <version>{{latest_version}}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ </dependencies>
+</dependencyManagement>and then use dependencies without specifying a version: +Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-mysql</artifactId>
+ <scope>test</scope>
+</dependency>Using Gradle 5.0 or higher, you can add the following to the dependencies section in your build.gradle:
Gradle
+implementation platform('org.testcontainers:testcontainers-bom:{{latest_version}}') //import bom
+testImplementation('org.testcontainers:testcontainers-mysql') //no version specifiedJitPack builds are available for pre-release versions.
+++Shaded dependencies +Testcontainers depends on other libraries (like docker-java) for it to work.
+
Some of them (JUnit, docker-java-{api,transport} and its transitive dependencies, JNA, visible-assertions and others) are part of our public API.
But there are also "private", implementation detail dependencies (e.g., docker-java-core, Guava, OkHttp, etc.) that are not exposed to public API but prone to conflicts with test code/application under test code. +As such, these libraries are 'shaded' into the core Testcontainers JAR and relocated underorg.testcontainers.shadedto prevent class conflicts.
A huge thank you to our sponsors:
+
+ Vivy
+ +
+ jOOQ
+ +
+ Backbase
+ +
+ Elastic
+ +
+ Red Hat
+ +
+ Spotify
+ +See LICENSE.
+This project includes a modified class (ScriptUtils) taken from the Spring JDBC project, adapted under the terms of the Apache license. Copyright for that class remains with the original authors.
+This project was initially inspired by a gist by Moshe Eshel.
+Copyright (c) 2015-2021 Richard North and other authors.
+See AUTHORS for contributors.
+You might want to use Testcontainers' database support:
+++Note +Of course, it's still important to have as few tests that hit the database as possible, and make good use of mocks for components higher up the stack.
+
See JDBC and R2DBC for information on how to use Testcontainers with SQL-like databases.
+You can obtain a temporary database in one of two ways:
+As long as you have Testcontainers and the appropriate JDBC driver on your classpath, you can simply modify regular JDBC connection URLs to get a fresh containerized instance of the database each time your application starts up.
+N.B:
+2.3.0) you need to specify the driver manually spring.datasource.driver-class-name=org.testcontainers.jdbc.ContainerDatabaseDriverOriginal URL: jdbc:mysql://localhost:3306/databasename
Insert tc: after jdbc: as follows. Note that the hostname, port and database name will be ignored; you can leave these as-is or set them to any value.
++Note +We will use
+///(host-less URIs) from now on to emphasis the unimportance of thehost:portpair.
From Testcontainers' perspective,jdbc:mysql:8.0.36://localhost:3306/databasenameandjdbc:mysql:8.0.36:///databasenameis the same URI.Warning +If you're using the JDBC URL support, there is no need to instantiate an instance of the container - Testcontainers will do it automagically.
+
jdbc:tc:clickhouse:18.10.3:///databasename
jdbc:tc:cockroach:v21.2.3:///databasename
jdbc:tc:cratedb:5.2.3:///databasename
jdbc:tc:db2:11.5.0.0a:///databasename
jdbc:tc:mariadb:10.3.39:///databasename
jdbc:tc:mysql:8.0.36:///databasename
jdbc:tc:sqlserver:2017-CU12:///databasename
jdbc:tc:oceanbasece:4.2.1-lts:///databasename
jdbc:tc:oracle:21-slim-faststart:///databasename
jdbc:tc:postgis:9.6-2.5:///databasename
jdbc:tc:postgresql:9.6.8:///databasename
jdbc:tc:questdb:6.5.3:///databasename
jdbc:tc:timescaledb:2.1.0-pg13:///databasename
jdbc:tc:pgvector:pg16:///databasename
jdbc:tc:tidb:v6.1.0:///databasename
jdbc:tc:timeplus:2.3.21:///databasename
jdbc:tc:trino:352://localhost/memory/default
jdbc:tc:yugabyte:2.14.4.0-b26:///databasename
Testcontainers can run an init script after the database container is started, but before your code is given a connection to it. The script must be on the classpath, and is referenced as follows:
+jdbc:tc:mysql:8.0.36:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql
This is useful if you have a fixed script for setting up database schema, etc.
+If the init script path is prefixed file:, it will be loaded from a file (relative to the working directory, which will usually be the project root).
jdbc:tc:mysql:8.0.36:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql
Instead of running a fixed script for DB setup, it may be useful to call a Java function that you define. This is intended to allow you to trigger database schema migration tools. To do this, add TC_INITFUNCTION to the URL as follows, passing a full path to the class name and method:
+ jdbc:tc:mysql:8.0.36:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction
The init function must be a public static method which takes a java.sql.Connection as its only parameter, e.g.
public class JDBCDriverTest {
+ public static void sampleInitFunction(Connection connection) throws SQLException {
+ // e.g. run schema setup or Flyway/liquibase/etc DB migrations here...
+ }
+ ...By default database container is being stopped as soon as last connection is closed. There are cases when you might need to start container and keep it running till you stop it explicitly or JVM is shutdown. To do this, add TC_DAEMON parameter to the URL as follows:
jdbc:tc:mysql:8.0.36:///databasename?TC_DAEMON=true
With this parameter database container will keep running even when there's no open connections.
+Container can have tmpfs mounts for storing data in host memory. This is useful if you want to speed up your database tests. Be aware that the data will be lost when the container stops.
To pass this option to the container, add TC_TMPFS parameter to the URL as follows:
jdbc:tc:postgresql:9.6.8:///databasename?TC_TMPFS=/testtmpfs:rw
If you need more than one option, separate them by comma (e.g. TC_TMPFS=key:value,key1:value1&other_parameters=foo).
For more information about tmpfs mount, see the official Docker documentation.
In case you can't use the URL support, or need to fine-tune the container, you can instantiate it yourself.
+Add a @Rule or @ClassRule to your test class, e.g.:
+public class SimpleMySQLTest {
+ @Rule
+ public MySQLContainer mysql = new MySQLContainer();Now, in your test code (or a suitable setup method), you can obtain details necessary to connect to this database:
+mysql.getJdbcUrl() provides a JDBC URL your code can connect tomysql.getUsername() provides the username your code should pass to the drivermysql.getPassword() provides the password your code should pass to the driverNote that if you use @Rule, you will be given an isolated container for each test method. If you use @ClassRule, you will get on isolated container for all the methods in the test class.
Examples/Tests:
+The MongoDB module provides two Testcontainers for MongoDB unit testing:
+The following example shows how to create a MongoDBContainer:
+ +Creating a MongoDB container inside_block:creatingMongoDBContainer
+ + +And how to start it:
+ +Starting a MongoDB container inside_block:startingMongoDBContainer
+ + +++Note +To construct a multi-node MongoDB cluster, consider the mongodb-replica-set project
+
Implement a reusable, cross-platform, simple to install solution that doesn't depend on +fixed ports to test MongoDB transactions.
+MongoDB starting from version 4 supports multi-document transactions only for a replica set. +For instance, to initialize a single node replica set on fixed ports via Docker, one has to do the following:
+As we can see, there is a lot of operations to execute and we even haven't touched a non-fixed port approach. +That's where the MongoDBContainer might come in handy.
+The following example shows how to create a MongoDBAtlasLocalContainer:
+ +Creating a MongoDB Atlas Local Container inside_block:creatingAtlasLocalContainer
+ + +And how to start it:
+ +Start the Container inside_block:startingAtlasLocalContainer
+ + +The connection string provided by the MongoDBAtlasLocalContainer's getConnectionString() method includes the dynamically allocated port:
+ +Get the Connection String inside_block:getConnectionStringAtlasLocalContainer
+ + +e.g. mongodb://localhost:12345/?directConnection=true
MongoDB Atlas Local combines the MongoDB database engine with MongoT, a sidecar process for advanced searching capabilities built by MongoDB and powered by Apache Lucene.
+The container (mongodb/mongodb-atlas-local) documentation can be found here.
+General information about Atlas Search can be found here.
+Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-mongodb:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-mongodb</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>++Hint +Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency
+
Copyright (c) 2019 Konstantin Silaev silaev256@gmail.com
+Testcontainers module for MySQL
+You can start a MySQL container instance from any Java application by using:
+ +Container definition inside_block:container
+ + +See Database containers for documentation and usage that is common to all relational database container types.
+jdbc:tc:mysql:8.0.36:///databasename
See JDBC for documentation.
+For MySQL databases, it is possible to override configuration settings using resources on the classpath. Assuming somepath/mysql_conf_override
+is a directory on the classpath containing .cnf files, the following URL can be used:
jdbc:tc:mysql:8.0.36://hostname/databasename?TC_MY_CNF=somepath/mysql_conf_override
Any .cnf files in this classpath directory will be mapped into the database container's /etc/mysql/conf.d directory, +and will be able to override server settings when the container starts.
+root user passwordIf no custom password is specified, the container will use the default user password test for the root user as well.
+When you specify a custom password for the database user,
+this will also act as the password of the MySQL root user automatically.
Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-mysql:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-mysql</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>++Hint +Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency.
+
Testcontainers module for PostgresSQL
+You can start a PostgreSQL container instance from any Java application by using:
+ +Container creation inside_block:container
+ + +See Database containers for documentation and usage that is common to all relational database container types.
+jdbc:tc:postgresql:9.6.8:///databasenamejdbc:tc:postgis:9.6-2.5:///databasenamejdbc:tc:timescaledb:2.1.0-pg13:///databasenamejdbc:tc:pgvector:pg16:///databasenameSee JDBC for documentation.
+PostgreSQLContainer can also be used with the following images:
Using pgvector inside_block:pgvectorContainer
+ + + + +Using PostGIS inside_block:postgisContainer
+ + + + +Using TimescaleDB inside_block:timescaledbContainer
+ + +Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-postgresql:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-postgresql</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>++Hint +Adding this Testcontainers library JAR will not automatically add a database driver JAR to your project. You should ensure that your project also has a suitable database driver as a dependency.
+
Testcontainers can be used to automatically instantiate and manage Apache Kafka containers.
+Currently, two different Kafka images are supported:
+org.testcontainers.kafka.ConfluentKafkaContainer supports
+confluentinc/cp-kafkaorg.testcontainers.kafka.KafkaContainer supports apache/kafka and apache/kafka-native++Note +
+org.testcontainers.containers.KafkaContaineris deprecated. +Please useorg.testcontainers.kafka.ConfluentKafkaContainerororg.testcontainers.kafka.KafkaContainerinstead, depending on the used image.
Create a KafkaContainer to use it in your tests:
Creating a KafkaContainer inside_block:constructorWithVersion
+ + +Now your tests or any other process running on your machine can get access to running Kafka broker by using the following bootstrap server location:
+ +Bootstrap Servers inside_block:getBootstrapServers
+ + +++Note +Compatible with
+confluentinc/cp-kafkaimages version7.4.0and later.
Create a ConfluentKafkaContainer to use it in your tests:
Creating a ConfluentKafkaContainer inside_block:constructorWithVersion
+ + +++Note +Only available for
+org.testcontainers.containers.KafkaContainer
KRaft mode was declared production ready in 3.3.1 (confluentinc/cp-kafka:7.3.x)
+ +Kraft mode inside_block:withKraftMode
+ + +See the versions interoperability matrix for more details.
+There are scenarios where additional listeners are needed because the consumer/producer can be in another +container in the same network or a different process where the port to connect differs from the default exposed port. E.g Toxiproxy.
+ +Register additional listener inside_block:registerListener
+ + +Container defined in the same network:
+ +Create kcat container inside_block:createKCatContainer
+ + +Client using the new registered listener:
+ +Produce/Consume via new listener inside_block:produceConsumeMessage
+ + +Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-kafka:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-kafka</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>Testcontainers module for LocalStack, 'a fully functional local AWS cloud stack', to develop and test your cloud and serverless apps without actually using the cloud.
+You can start a LocalStack container instance from any Java application by using:
+ +Container creation inside_block:container
+ + +Environment variables listed in the LocalStack configuration documentation may be used to customize LocalStack's configuration.
+Use the .withEnv(key, value) method on LocalStackContainer to apply configuration settings.
++Note +Starting March 23, 2026,
+localstack/localstackrequires authentication via aLOCALSTACK_AUTH_TOKENenvironment variable. Without it, the container will fail to start.Use
+.withEnv("LOCALSTACK_AUTH_TOKEN", System.getenv("LOCALSTACK_AUTH_TOKEN"))to pass the token. +See the LocalStack blog post for more details.
AWS SDK V2 inside_block:with_aws_sdk_v2
+ + +Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-localstack:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-localstack</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>Testcontainers module for Shopify's Toxiproxy. +This TCP proxy can be used to simulate network failure conditions.
+You can simulate network failures:
+Testcontainers Toxiproxy support allows resilience features to be easily verified as part of isolated dev/CI testing. This allows earlier testing of resilience features, and broader sets of failure conditions to be covered.
+A Toxiproxy container can be placed in between test code and a container, or in between containers.
+In either scenario, it is necessary to create a ToxiproxyContainer instance on the same Docker network, as follows:
Creating a Toxiproxy container inside_block:creatingProxy
+ + +Next, it is necessary to instruct Toxiproxy to start proxying connections.
+Each ToxiproxyContainer can proxy to many target containers if necessary.
We do this as follows:
+ +Starting proxying connections to a target container inside_block:obtainProxyObject
+ + +To establish a connection from the test code (on the host machine) to the target container via Toxiproxy, we obtain Toxiproxy's proxy host IP and port:
+ +Obtaining proxied host and port inside_block:obtainProxiedHostAndPortForHostMachine
+ + +Code under test should connect to this proxied host IP and port.
+++Note +Currently,
+ToxiProxyContainerwill reserve 31 ports, starting at 8666.
Other containers should connect to this proxied host and port.
+Having done this, it is possible to trigger failure conditions ('Toxics') through the proxy.toxics() object:
bandwidth - Limit a connection to a maximum number of kilobytes per second.latency - Add a delay to all data going through the proxy. The delay is equal to latency +/- jitter.slicer - Slices TCP data up into small bits, optionally adding a delay between each sliced "packet".slowClose - Delay the TCP socket from closing until delay milliseconds has elapsed.timeout - Stops all data from getting through, and closes the connection after timeout. If timeout is 0, the connection won't close, and data will be delayed until the toxic is removed.limitData - Closes connection when transmitted data exceeded limit.Please see the Toxiproxy documentation for full details on the available Toxics.
+As one example, we can introduce latency and random jitter to proxied connections as follows:
+ +Adding latency to a connection inside_block:addingLatency
+ + +Additionally we can disable the proxy to simulate a complete interruption to the network connection:
+ +Cutting a connection inside_block:disableProxy
+ + +Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-toxiproxy:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-toxiproxy</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>This module was inspired by a hotels.com blog post.
+It's easy to add Testcontainers to your project - let's walk through a quick example to see how.
+Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it.
+In our imaginary program, there is a RedisBackedCache class which stores data in Redis.
You can see an example test that could have been written for it (without using Testcontainers):
+ +Pre-Testcontainers test code block:RedisBackedCacheIntTestStep0
+ + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes.
+Let's start from here, and see how to improve the test with Testcontainers:
+First, add Testcontainers as a dependency as follows:
+Gradle
+testImplementation "org.testcontainers:testcontainers:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>Simply add the following to the body of our test class:
+ +JUnit 4 Rule inside_block:rule
+ + +The @Rule annotation tells JUnit to notify this field about various events in the test lifecycle.
+In this case, our rule object is a Testcontainers GenericContainer, configured to use a specific Redis image from Docker Hub, and configured to expose a port.
If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers:
+Before Testcontainers, we might have hardcoded an address like localhost:6379 into our tests.
Testcontainers uses randomized ports for each container it starts, but makes it easy to obtain the actual port at runtime.
+We can do this in our test setUp method, to set up our component under test:
Obtaining a mapped port inside_block:setUp
+ + +++Tip +Notice that we also ask Testcontainers for the container's actual address with
+redis.getHost();, +rather than hard-codinglocalhost.localhostmay work in some environments but not others - for example it may +not work on your current or future CI environment. As such, avoid hard-coding the address, and use +getHost()instead.
That's it!
+Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers:
+ +RedisBackedCacheIntTest block:RedisBackedCacheIntTest
+ + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how.
+Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it.
+In our imaginary program, there is a RedisBackedCache class which stores data in Redis.
You can see an example test that could have been written for it (without using Testcontainers):
+ +Pre-Testcontainers test code block:RedisBackedCacheIntTestStep0
+ + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes.
+Let's start from here, and see how to improve the test with Testcontainers:
+First, add Testcontainers as a dependency as follows:
+Gradle
+testImplementation "org.junit.jupiter:junit-jupiter:5.8.1"
+testImplementation "org.testcontainers:testcontainers:{{latest_version}}"
+testImplementation "org.testcontainers:testcontainers-junit-jupiter:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.junit.jupiter</groupId>
+ <artifactId>junit-jupiter</artifactId>
+ <version>5.8.1</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-junit-jupiter</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>First, you'll need to annotate the test class with @Testcontainers. Furthermore, add the following to the body of our test class:
JUnit 5 Rule inside_block:container
+ + +The @Container annotation tells JUnit to notify this field about various events in the test lifecycle.
+In this case, our rule object is a Testcontainers GenericContainer, configured to use a specific Redis image from Docker Hub, and configured to expose a port.
If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers:
+Before Testcontainers, we might have hardcoded an address like localhost:6379 into our tests.
Testcontainers uses randomized ports for each container it starts, but makes it easy to obtain the actual port at runtime.
+We can do this in our test setUp method, to set up our component under test:
Obtaining a mapped port inside_block:setUp
+ + +++Tip +Notice that we also ask Testcontainers for the container's actual address with
+redis.getHost();, +rather than hard-codinglocalhost.localhostmay work in some environments but not others - for example it may +not work on your current or future CI environment. As such, avoid hard-coding the address, and use +getHost()instead.
Additional attributes are available for the @Testcontainers annotation.
+Those attributes can be helpful when:
disabledWithoutDocker to true.parallel to true.That's it!
+Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers:
+ +RedisBackedCacheIntTest inside_block:class
+ + +It's easy to add Testcontainers to your project - let's walk through a quick example to see how.
+Let's imagine we have a simple program that has a dependency on Redis, and we want to add some tests for it.
+In our imaginary program, there is a RedisBackedCache class which stores data in Redis.
You can see an example test that could have been written for it (without using Testcontainers):
+ +Pre-Testcontainers test code block:RedisBackedCacheIntTestStep0
+ + +Notice that the existing test has a problem - it's relying on a local installation of Redis, which is a red flag for test reliability. +This may work if we were sure that every developer and CI machine had Redis installed, but would fail otherwise. +We might also have problems if we attempted to run tests in parallel, such as state bleeding between tests, or port clashes.
+Let's start from here, and see how to improve the test with Testcontainers:
+First, add Testcontainers as a dependency as follows:
+Gradle
+testImplementation "org.testcontainers:testcontainers-spock:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-spock</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>Annotate the Spock specification class with the Testcontainers extension:
+Spock Testcontainers annotation
+@org.testcontainers.spock.Testcontainers
+class RedisBackedCacheIntTest extends Specification {And add the following field to the body of our test class:
+ +Spock Testcontainers init inside_block:init
+ + +This tells Spock to start a Testcontainers GenericContainer, configured to use a specific Redis image from Docker Hub, and configured to expose a port.
If we run our test as-is, then regardless of the actual test outcome, we'll see logs showing us that Testcontainers:
+Before Testcontainers, we might have hardcoded an address like localhost:6379 into our tests.
Testcontainers uses randomized ports for each container it starts, but makes it easy to obtain the actual port at runtime.
+We can do this in our test setup method, to set up our component under test:
Obtaining a mapped port inside_block:setup
+ + +++Tip +Notice that we also ask Testcontainers for the container's actual address with
+redis.containerIpAddress, +rather than hard-codinglocalhost.localhostmay work in some environments but not others - for example it may +not work on your current or future CI environment. As such, avoid hard-coding the address, and use +containerIpAddressinstead.
That's it!
+Let's look at our complete test class to see how little we had to add to get up and running with Testcontainers:
+ +RedisBackedCacheIntTest block:complete
+ +This module provides an API that is based on the JUnit Jupiter extension model.
+The extension supports two modes:
+Note that Jupiter/JUnit 5 integration is packaged as a separate library JAR; see below for details.
+Jupiter integration is provided by means of the @Testcontainers annotation.
The extension finds all fields that are annotated with @Container and calls their container lifecycle
+methods (methods on the Startable interface). Containers declared as static fields will be shared between test
+methods. They will be started only once before any test method is executed and stopped after the last test method has
+executed. Containers declared as instance fields will be started and stopped for every test method.
Note: This extension has only been tested with sequential test execution. Using it with parallel test execution is +unsupported and may have unintended side effects.
+Example:
+ +Mixed Lifecycle inside_block:testClass
+ + +To use the Testcontainers extension annotate your test class with @Testcontainers.
To define a restarted container, define an instance field inside your test class and annotate it with
+the @Container annotation.
Restarted Containers inside_block:testClass
+ + + +Shared containers are defined as static fields in a top level test class and have to be annotated with @Container.
+Note that shared containers can't be declared inside nested test classes.
+This is because nested test classes have to be defined non-static and can't therefore have static fields.
Shared Container lines:18-23,32-33,35-36
+ + +Note that the singleton container pattern is also an option when +using JUnit 5.
+This extension has only been tested with sequential test execution. Using it with parallel test execution is unsupported and may have unintended side effects.
+Add the following dependency to your pom.xml/build.gradle file:
Gradle
+testImplementation "org.testcontainers:testcontainers-junit-jupiter:{{latest_version}}"Maven
+<dependency>
+ <groupId>org.testcontainers</groupId>
+ <artifactId>testcontainers-junit-jupiter</artifactId>
+ <version>{{latest_version}}</version>
+ <scope>test</scope>
+</dependency>