Skip to content

Modules

Modules are great examples of Testcontainers' capabilities. To write tests against real dependencies, you can either choose one of the pre-configurations listed below or create your own implementation.

Modules are standalone dependencies that can be installed from NuGet.org. To use a module in your test project, you need to add it as a dependency first:

1
dotnet add package Testcontainers.ModuleName

All modules follow the same design and come pre-configured with best practices. Usually, you do not need to worry about configuring them yourself. To create and start a container, all you need is:

1
2
var moduleNameContainer = new ModuleNameBuilder().Build();
await moduleNameContainer.StartAsync();

Note

We will add module-specific documentations soon.

Module Image NuGet Source
ActiveMQ Artemis apache/activemq-artemis:2.31.2 NuGet Source
ArangoDB arangodb:3.11.5 NuGet Source
Azure Cosmos DB mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest NuGet Source
Azurite mcr.microsoft.com/azure-storage/azurite:3.24.0 NuGet Source
BigQuery ghcr.io/goccy/bigquery-emulator:0.4 NuGet Source
Bigtable gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators NuGet Source
ClickHouse clickhouse/clickhouse-server:23.6-alpine NuGet Source
CockroachDB cockroachdb:23.1.13 NuGet Source
Consul consul:1.15 NuGet Source
Couchbase couchbase:community-7.0.2 NuGet Source
CouchDB couchdb:3.3 NuGet Source
DynamoDB amazon/dynamodb-local:1.21.0 NuGet Source
Elasticsearch elasticsearch:8.6.1 NuGet Source
EventStoreDB eventstore/eventstore:22.10.1-buster-slim NuGet Source
FakeGcsServer fsouza/fake-gcs-server:1.47 NuGet Source
Firebird jacobalberty/firebird:v4.0 NuGet Source
Firestore gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators NuGet Source
InfluxDB influxdb:2.7 NuGet Source
JanusGraph janusgraph/janusgraph:1.0.0 NuGet Source
K3s rancher/k3s:v1.26.2-k3s1 NuGet Source
Kafka confluentinc/cp-kafka:6.1.9 NuGet Source
Keycloak quay.io/keycloak/keycloak:21.1 NuGet Source
Kusto emulator mcr.microsoft.com/azuredataexplorer/kustainer-linux:latest NuGet Source
LocalStack localstack/localstack:2.0 NuGet Source
MariaDB mariadb:10.10 NuGet Source
Milvus milvusdb/milvus:v2.3.10 NuGet Source
MinIO minio/minio:RELEASE.2023-01-31T02-24-19Z NuGet Source
MongoDB mongo:6.0 NuGet Source
MySQL mysql:8.0 NuGet Source
NATS nats:2.9 NuGet Source
Neo4j neo4j:5.4 NuGet Source
Oracle gvenzl/oracle-xe:21.3.0-slim-faststart NuGet Source
Papercut changemakerstudiosus/papercut-smtp:latest NuGet Source
PostgreSQL postgres:15.1 NuGet Source
PubSub gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators NuGet Source
Pulsar apachepulsar/pulsar:3.0.6 NuGet Source
RabbitMQ rabbitmq:3.11 NuGet Source
RavenDB ravendb/ravendb:5.4-ubuntu-latest NuGet Source
Redis redis:7.0 NuGet Source
Redpanda docker.redpanda.com/redpandadata/redpanda:v22.2.1 NuGet Source
SQL Server mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04 NuGet Source
WebDriver selenium/standalone-chrome:110.0 NuGet Source

Implement a module

The Testcontainers for .NET repository contains a .NET template to scaffold advanced modules quickly. To create and add a new module to the Testcontainers solution file, checkout the repository and install the .NET template first:

1
2
3
git clone --branch develop [email protected]:testcontainers/testcontainers-dotnet.git
cd ./testcontainers-dotnet/
dotnet new --install ./src/Templates

The following CLI commands create and add a new PostgreSQL module to the solution file:

1
2
dotnet new tcm --name PostgreSql --official-module true --output ./src
dotnet sln add ./src/Testcontainers.PostgreSql/Testcontainers.PostgreSql.csproj

Note

If you decided to publish a module to NuGet.org on your own, you need to set the --official-module flag to false.

A module typically consists of three classes representing the builder, the configuration and the container. The PostgreSQL module we just created above consists of the PostgreSqlBuilder, PostgreSqlConfiguration and PostgreSqlContainer classes.

  1. The builder class sets the module default configuration and validates it. It extends the Testcontainers builder and adds or overrides members specifically to configure the module. The builder is responsible for creating a valid configuration and container instance.
  2. The configuration class stores optional members to configure the module and interact with the container. Usually, these are properties like a Username or Password that are required sometime later.
  3. Developers interact with the builder the most. It manages the lifecycle and provides module specific members to interact with the container. The result of the builder is an instance of the container class.

Configure a module

The configuration classes in Testcontainers are designed to be immutable, meaning that once an instance of a configuration class has been created, its values cannot be changed. This has a number of benefits, it is more reliable, easier to understand and to share between different use cases like A/B testing.

To set the PostgreSQL module default configuration, override the read-only DockerResourceConfiguration property in PostgreSqlBuilder and set its value in both constructors. The default constructor sets DockerResourceConfiguration to the return value of Init().DockerResourceConfiguration, where the overloaded private constructor just sets the argument value. It receives an updated instance of the immutable Docker resource configuration as soon as a property changes.

The .NET template already includes this configuration, making it easy for developers to quickly get started by simply commenting out the necessary parts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public PostgreSqlBuilder()
  : this(new PostgreSqlConfiguration())
{
  DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

private PostgreSqlBuilder(PostgreSqlConfiguration resourceConfiguration)
  : base(resourceConfiguration)
{
  DockerResourceConfiguration = resourceConfiguration;
}

protected override PostgreSqlConfiguration DockerResourceConfiguration { get; }

To append the PostgreSQL configurations to the default Testcontainers configurations override or comment out the Init() member and add the necessary configurations such as the Docker image and a wait strategy to the base implementation:

1
2
3
4
5
protected override PostgreSqlBuilder Init()
{
  var waitStrategy = Wait.ForUnixContainer().UntilCommandIsCompleted("pg_isready");
  return base.Init().WithImage("postgres:15.1").WithPortBinding(5432, true).WithWaitStrategy(waitStrategy);
}

Extend a module

When using the PostgreSQL Docker image, it is required to have a password set in order to run it. To demonstrate how to add a new builder member, we will use this requirement as an example.

First add a new property Password to the PostgreSqlConfiguration class. Add a password argument with a default value of null to the default constructor. This allows the builder to set individual arguments or configurations.

The overloaded PostgreSqlConfiguration(PostgreSqlConfiguration, PostgreSqlConfiguration) constructor takes care of merging the configurations together. The builder will receive and hold an updated instances that contains all information:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public PostgreSqlConfiguration(string password = null)
{
  Password = password;
}

public PostgreSqlConfiguration(PostgreSqlConfiguration oldValue, PostgreSqlConfiguration newValue)
  : base(oldValue, newValue)
{
  Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
}

public string Password { get; }

Since the PostgreSqlConfiguration class is now able to store the password value, we can add a member WithPassword(string) to PostgreSqlBuilder. We not just store the password in the PostgreSqlConfiguration instance to construct the database connection string later, but we also set the necessary environment variable POSTGRES_PASSWORD to run the container:

1
2
3
4
public PostgreSqlBuilder WithPassword(string password)
{
  return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(password: password)).WithEnvironment("POSTGRES_PASSWORD", password);
}

By following this approach, the PostgreSqlContainer class is able to access the configured values, allowing it to provide additional functionalities, such as constructing the database connection string. This enables the class to provide a more streamlined and convenient experience for developers who are working with modules:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public string GetConnectionString()
{
  var properties = new Dictionary<string, string>();
  properties.Add("Host", Hostname);
  properties.Add("Port", GetMappedPublicPort(5432).ToString());
  properties.Add("Database", "postgres");
  properties.Add("Username", "postgres");
  properties.Add("Password", _configuration.Password);
  return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
}

Finally, there are two approaches to ensure that the required password is provided. Either override the Validate() member and check the immutable configuration instance:

1
2
3
4
5
6
7
8
protected override void Validate()
{
  base.Validate();

  _ = Guard.Argument(DockerResourceConfiguration.Password, nameof(PostgreSqlConfiguration.Password))
    .NotNull()
    .NotEmpty();
}

or extend the Init() member as we have already done and add WithPassword(Guid.NewGuid().ToString()) to set a default value.

It is always a good idea to add both approaches. This way, the user can be sure that the module is properly configured, whether by themself or by default. This helps maintain a consistent and reliable experience for the user.

The repository provides reference implementations of modules. This modules are comprehensive examples and can serve as guides for you to get a better understanding of how to implement an entire module including the tests.