I had the same problem. I hope to give you some info to solve it.
If you try to build the docker image using the command docker build -t my-image-name .
, you will notice that the build process fails at dotnet test
.
The reason for the failure is that TestContainer is trying to run a second Docker container, but it is unable to communicate with Dockerd.
If you look at some websites (even the TestContainer dind docs), they suggest adding the volume -v /var/run/docker.sock:/var/run/docker.sock
, as you found in the docker compose you posted.
Note that this solution only works if you are running dotnet test
through a docker run
because it is not possible to add an external volume during a docker build
.
Same problem if you try to run docker-compose up. You docker-compose has a build section where it says to build a container from the dockerfile you created, and it crashes as described above.
Why does it work when you run tests using the VS testrunner? it works because you testrunner is running dotnet test
in an environment where dockerd is already present.
Possible solution: using a multi stage dockefile.
as we can see the problem is the execution of dotnet test during the docker build process. We must execute after it.
A multi stage dockerfile can help us. Take this dockerfile as example:
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS base
WORKDIR /App
COPY . ./
RUN dotnet restore
RUN dotnet build
FROM base as test
CMD [ "dotnet", "test" ]
# multiple stages (like publish and so on)
This dockerfile declares at least 2 stages: base and test.
Note that dotnet test
is no longer run during the build process, but as a command whenever you run the container from the test stage.
execute the command:
docker build -t my-image-name --target test .
to create an image of your application up to the test stage
now execute the command:
docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock --name my-container-name my-image-name
And Voilà your tests are working and TestContainer is able to run another docker container!
Then you can proceed and build your whole image executing the command:
docker build -t my-image-name .
Docker Compose Solution
A solution for Docker Compose might be as follows:
- Create a dockerfile for testing purposes only
- Run your containers if and only if the container based on the test dockerfile completes successfully.
Here is an example based on the dockerfile above. My docker-compose.yml will run the container "docker_hello_world" if and only if the container built from my dockerfile (to run the dotnet test) completes successfully.
version: "3.8"
services:
docker_hello_world:
image: hello-world
depends_on:
docker_compose_test:
condition: service_completed_successfully
docker_compose_test:
build:
dockerfile: Dockerfile
context: .
volumes:
- /var/run/docker.sock:/var/run/docker.sock
At your end you will need
- a dockerfile that contains the definition to create an image that can be run with the
dotnet test
command.
- a dockerfile containing the definition to build an image for use in production (without running dotnet test)
- a docker-compose.yml where your production container depends on the result of running the container based on the docker image created for testing purposes.
I hope I helped you in some way. let me know if you need more info.
some references:
https://dotnet.testcontainers.org/examples/dind/
https://docs.docker.com/build/building/multi-stage/
https://docs.docker.com/language/java/run-tests/
https://docs.docker.com/compose/startup-order/