Testcontainers with WSL

23 Jul, 2023
C#.NETTestsWSLDocker

How to run integration tests using Testcontainers with WSL

The purpose of this demo is to demonstrate how to run integration tests using Testcontainers with WSL. I won't explain how you should write your tests using Testcontainers, my goal is just to show how to run them in WSL.

Demo

Issue

Some people, when developing on Windows, use Docker Desktop, and in that case, the Docker daemon is running on the same machine as the IDE, and the tests are executed without any problem. However, when you are using WSL, the Docker daemon is running on a different machine, and in this case, Testcontainers cannot start the containers because it can't connect to the Docker daemon.

Solution

1. Expose the endpoint of the Docker daemon

In WSL, create a file named daemon.json in the folder /etc/docker/ with the following content:

{
  "hosts": [
    "tcp://0.0.0.0:2375",
    "unix:///var/run/docker.sock"
  ]
}

After that, restart the Docker daemon or WSL.

The previous step was obtained from here.

2. Configure testcontainers in test project

In your integration tests project, you should have an extension of the WebApplicationFactory with configurations for Testcontainers similar to this:

private readonly IContainer _dbContainer = new ContainerBuilder()
    .WithImage(DB_IMAGE)
    .WithName($"db-integration-tests-{Guid.NewGuid().ToString()[^5..]}")
    .WithEnvironment("MYSQL_ROOT_PASSWORD", DB_ROOT_PASSWORD)
    .WithEnvironment("MYSQL_DATABASE", DB_DATABASE)
    .WithPortBinding(DB_HOST_PORT, DB_CONTAINER_PORT)
    .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(DB_CONTAINER_PORT))
    .Build();

You just need to add the configuration of the Docker endpoint WithDockerEndpoint(), like this:

private readonly IContainer _dbContainer = new ContainerBuilder()
    .WithImage(DB_IMAGE)
    .WithName($"db-integration-tests-{Guid.NewGuid().ToString()[^5..]}")
#if RUN_LOCAL
    .WithDockerEndpoint("tcp://localhost:2375")
#endif
    .WithEnvironment("MYSQL_ROOT_PASSWORD", DB_ROOT_PASSWORD)
    .WithEnvironment("MYSQL_DATABASE", DB_DATABASE)
    .WithPortBinding(DB_HOST_PORT, DB_CONTAINER_PORT)
    .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(DB_CONTAINER_PORT))
    .Build();

The RUN_LOCAL is a compilation symbol that I use to run the tests locally. In the CI pipeline, I don't use it, so the tests are executed without the WithDockerEndpoint() configuration.

If you also want to run the conditional compilation symbol, you need to configure your symbol in the .csproj file of the integration tests project, like this:

<PropertyGroup Condition="'$(COMPUTERNAME)' != ''">
  <DefineConstants>$(DefineConstants);RUN_LOCAL</DefineConstants>
</PropertyGroup>

In that configuration, we use the COMPUTERNAME environment variable to check if the tests are running locally or not, because in the CI pipeline, this variable is not defined. With this approach, we can set the RUN_LOCAL compilation symbol only when the tests are running locally.

The remaining configurations of the Testcontainers are normal configurations that you also use when running directly on the host machine.

We are using cookies to ensure that we give you the best experience on our website. By clicking "Accept", you consent to the use of ALL the cookies. However you may visit Cookie policy to provide a controlled consent.