Image name substitution
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:
- Manual substitution - not relying upon an automated approach
- Using an Image Name Substitutor:
It is assumed that you have already set up a private registry hosting all the Docker images your build requires.
Manual substitution
Consider this if:
- You use only a few images and updating code is not a chore
- All developers and CI machines in your organisation have access to a common registry server
- You also use one of the automated mechanisms to substitute the images that Testcontainers itself requires
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:
// Referring directly to an image on Docker Hub (mysql:8.0.24)
final MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName.parse("mysql:8.0.24")
)
// start the container and use it for testing
to:
// Referring directly to an image on a private registry - image name will vary
final MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName.parse("registry.mycompany.com/mirror/mysql:8.0.24")
.asCompatibleSubstituteFor("mysql")
)
// start the container and use it for testing
Automatically modifying Docker Hub image names
Testcontainers can be configured to modify Docker Hub image names on the fly to apply a prefix string.
Consider this if:
- Developers and CI machines need to use different image names. For example, developers are able to pull images from Docker Hub, but CI machines need to pull from a private registry
- Your private registry has copies of images from Docker Hub where the names are predictable, and just adding a prefix is enough.
For example,
registry.mycompany.com/mirror/mysql:8.0.24
can be derived from the original Docker Hub image name (mysql:8.0.24
) 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:
// Referring directly to an image on Docker Hub (mysql:8.0.24)
final MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName.parse("mysql:8.0.24")
)
// start the container and use it for testing
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:
- Setting environment variables
TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=registry.mycompany.com/mirror/
- Via config file, setting
hub.image.name.prefix
in either:- the
~/.testcontainers.properties
file in your user home directory, or - a file named
testcontainers.properties
on the classpath
- the
Testcontainers 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:
- non-Hub image names (e.g. where another registry is set)
- Docker Hub image names where the hub registry is explicitly part of the name (i.e. anything with a
docker.io
orregistry.hub.docker.com
host part)
Developing a custom function for transforming image names on the fly
Consider this if:
- You have complex rules about which private registry images should be used as substitutes, e.g.:
- non-deterministic mapping of names meaning that a name prefix cannot be used
- rules depending upon developer identity or location
- or you wish to add audit logging of images used in the build
- or you wish to prevent accidental usage of images that are not on an approved list
In this case, image name references in code are unchanged. i.e. you would leave as-is:
// Referring directly to an image on Docker Hub (mysql:8.0.24)
final MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName.parse("mysql:8.0.24")
)
// start the container and use it for testing
You can implement a custom image name substitutor by:
- subclassing
org.testcontainers.utility.ImageNameSubstitutor
- configuring Testcontainers to use your custom implementation
The following is an example image substitutor implementation:
public class ExampleImageNameSubstitutor extends ImageNameSubstitutor {
@Override
public DockerImageName apply(DockerImageName original) {
// convert the original name to something appropriate for
// our build environment
return DockerImageName.parse(
// your code goes here - silly example of capitalising
// the original name is shown
original.asCanonicalNameString().toUpperCase()
);
}
@Override
protected String getDescription() {
// used in logs
return "example image name substitutor";
}
}
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:
image.substitutor=com.mycompany.testcontainers.ExampleImageNameSubstitutor
Note that it is also possible to provide this same configuration property:
- in a
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) - in a properties file in the user's home directory (
~/.testcontainers.properties
; note the leading.
) - or as an environment variable (e.g.
TESTCONTAINERS_IMAGE_SUBSTITUTOR=com.mycompany.testcontainers.ExampleImageNameSubstitutor
).
Please see the documentation on configuration mechanisms for more information.
Overriding image names individually in configuration
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:
- You have many references to image names in code and changing them is impractical, and
- None of the other options are practical for you
In this case, image name references in code are left unchanged. i.e. you would leave as-is:
// Referring directly to an image on Docker Hub (mysql:8.0.24)
final MySQLContainer<?> mysql = new MySQLContainer<>(
DockerImageName.parse("mysql:8.0.24")
)
// start the container and use it for testing
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.