diff --git a/samples/complexapp/Dockerfile b/samples/complexapp/Dockerfile index 79e3212e6a..9cdeca7c72 100644 --- a/samples/complexapp/Dockerfile +++ b/samples/complexapp/Dockerfile @@ -1,5 +1,7 @@ # Learn about building .NET container images: # https://github.com/dotnet/dotnet-docker/blob/main/samples/README.md + +# build copies all project files and restores NuGet packages FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:9.0 AS build ARG TARGETARCH WORKDIR /source @@ -15,9 +17,9 @@ COPY --link complexapp/ complexapp/ COPY --link libfoo/ libfoo/ COPY --link libbar/ libbar/ -# Test stage -- exposes optional entrypoint -# Target entrypoint with: docker build --target test -FROM build AS test + +# test-build builds the xUnit test project +FROM build AS test-build COPY --link tests/*.csproj tests/ WORKDIR /source/tests @@ -26,15 +28,20 @@ RUN dotnet restore COPY --link tests/ . RUN dotnet build --no-restore -ENTRYPOINT ["dotnet", "test", "--logger:trx", "--no-build"] +# test-entrypoint exposes tests as the default executable for the stage +FROM test-build AS test +ENTRYPOINT ["dotnet", "test", "--no-build", "--logger:trx"] + +# publish builds and publishes complexapp FROM build AS publish WORKDIR /source/complexapp RUN dotnet publish -a $TARGETARCH --no-restore -o /app -# Runtime stage -FROM mcr.microsoft.com/dotnet/runtime:9.0 + +# final is the final runtime stage for running the app +FROM mcr.microsoft.com/dotnet/runtime:9.0 AS final WORKDIR /app COPY --link --from=publish /app . ENTRYPOINT ["dotnet", "complexapp.dll"] diff --git a/samples/complexapp/README.md b/samples/complexapp/README.md index 97a62e03d1..435cbc603a 100644 --- a/samples/complexapp/README.md +++ b/samples/complexapp/README.md @@ -22,7 +22,7 @@ In the case of an application with multiple project dependencies, it may be intu Instead, in a multi-project solution, the best pattern is to place the Dockerfile at the same location you would place a solution file, which is typically one directory above the application project. At that location, you can use the pattern demonstrated above, using `.` for the build context at the same location as the Dockerfile, and enabling all resources to be naturally located within that context. -This is the approach used with [complexapp](.). The [Dockerfile](Dockerfile) for the sample is at the same location as the `.sln` file, and all projects are available when that same location is used as the build context. There are other options, but this approach is the easiest. +This is the approach used with [complexapp](.). The [Dockerfile](./Dockerfile) for the sample is at the same location as the `.sln` file, and all projects are available when that same location is used as the build context. There are other options, but this approach is the easiest. You can build and run the complexapp using the following commands: @@ -48,145 +48,10 @@ This is different than running tests within a [.NET SDK container](../run-tests- > [!NOTE] > See [Establishing docker environment](../establishing-docker-environment.md) for more information on correctly configuring Dockerfiles and `docker build` commands. -## Running tests as an opt-in stage +## Running tests -There are multiple approaches for testing with containers, such as the `ENTRYPOINT` of an opt-in stage (covered in this section) or as part of `docker build` (covered in the next section). The opt-in test stage approach covered in this section is the recommended approach because it is more flexible. - -The primary benefit of using an opt-in stage for testing is that it enables using the same environment as the build as an opt-in scenario and allows volume mounting (which isn't possible with `docker build`) to collect test logs. - -The [Dockerfile](Dockerfile) includes a `test` stage that demonstrates running via its `ENTRYPOINT`, as follows. - -```Dockerfile -# test stage -- exposes optional entrypoint -# target entrypoint with: docker build --target test -FROM build AS test - -COPY tests/*.csproj tests/ -WORKDIR /source/tests -RUN dotnet restore - -COPY tests/ . -RUN dotnet build --no-restore - -ENTRYPOINT ["dotnet", "test", "--logger:trx", "--no-build"] -``` - -The presence of the `test` stage costs very little and doesn't significantly change the behavior of the build if you don't specifically target it. By default, the test stage `ENTRYPOINT` will not be used if you build this Dockerfile - -The following example demonstrates targeting the `test` stage with the `--target` argument, and with logging enabled, using PowerShell: - -```console -PS C:\git\dotnet-docker\samples\complexapp> docker build --pull --target test -t complexapp-test . -Sending build context to Docker daemon 12.81MB -Step 1/15 : FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build -8.0: Pulling from dotnet/sdk -Successfully built f98c5453be3d -Successfully tagged complexapp-test:latest -SECURITY WARNING: You are building a Docker image from Windows against a non-Windows Docker host. All files and directories added to build context will have '-rwxr-xr-x' permissions. It is recommended to double check and reset permissions for sensitive files and directories. - -PS C:\git\dotnet-docker\samples\complexapp> mkdir TestResults - -PS C:\git\dotnet-docker\samples\complexapp> docker run --rm -v $pwd\TestResults:/source/tests/TestResults complexapp-test - Determining projects to restore... - Restored /source/tests/tests.csproj (in 7.73 sec). - 2 of 3 projects are up-to-date for restore. - libbar -> /source/libbar/bin/Debug/netstandard2.0/libbar.dll - libfoo -> /source/libfoo/bin/Debug/netstandard2.0/libfoo.dll - tests -> /source/tests/bin/Debug/net8.0/tests.dll -Test run for /source/tests/bin/Debug/net8.0/tests.dll (.NETCoreApp,Version=v8.0) -Microsoft (R) Test Execution Command Line Tool Version 17.10.0 -Copyright (c) Microsoft Corporation. All rights reserved. - -Starting test execution, please wait... -A total of 1 test files matched the specified pattern. -Results File: /source/tests/TestResults/_886d04dbf347_2020-11-02_18_30_59.trx - -Passed! - Failed: 0, Passed: 2, Skipped: 0, Total: 2, Duration: 2 ms - /source/tests/bin/Debug/net8.0/tests.dll (net8.0) - -PS C:\git\dotnet-docker\samples\complexapp> dir .\TestResults\ - - - Directory: C:\git\dotnet-docker\samples\complexapp\TestResults - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 9/2/2021 12:31 PM 3583 _886d04dbf347_2021-09-02_18_30_59.trx -``` - -The following instructions demonstrate this scenario in various configurations, with logging enabled. - -### Linux or macOS - -```console -docker build --pull --target test -t complexapp-test . -docker run --rm -v ${pwd}/TestResults:/source/tests/TestResults complexapp-test -``` - -### Windows using Linux containers - -The following example uses PowerShell. - -```console -docker build --pull --target test -t complexapp-test . -docker run --rm -v ${pwd}\TestResults:/source/tests/TestResults complexapp-test -``` - -### Windows using Windows containers - -The following example uses PowerShell. - -```console -docker build --pull --target test -t complexapp-test . -docker run --rm -v ${pwd}\TestResults:c:\source\tests\TestResults complexapp-test -``` - -## Running tests while building an image - -It is possible to run tests as part of `docker build`. This approach can be useful if you want `docker build` to fail if your tests fail. **This approach is not recommended**, as will be described later in this section. - -This approach can be implemented by adding the following instructions to the `publish` stage. It is not included in the sample Dockerfile because it is not the recommended approach. - -```Dockerfile -COPY tests/ /source/tests/ -WORKDIR /source/tests -RUN dotnet restore -RUN dotnet test --no-restore --logger:trx -``` - -If you build the [sample Dockerfile](Dockerfile) with this change, you will see that tests are run during the build: - -```console -> docker build --progress=plain -t complexapp . -#0 building with "desktop-linux" instance using docker driver - -#1 [internal] load build definition from Dockerfile -#1 transferring dockerfile: 1.28kB done - -... - -#22 [publish 6/6] RUN dotnet test --no-restore --logger:trx -#22 1.108 /usr/share/dotnet/sdk/9.0.100-rc.1.24452.12/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(326,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [/source/tests/tests.csproj] -#22 1.748 /usr/share/dotnet/sdk/9.0.100-rc.1.24452.12/Sdks/Microsoft.NET.Sdk/targets/Microsoft.NET.RuntimeIdentifierInference.targets(326,5): message NETSDK1057: You are using a preview version of .NET. See: https://aka.ms/dotnet-support-policy [/source/libbar/libbar.csproj] -#22 4.167 libfoo -> /source/libfoo/bin/Debug/net9.0/libfoo.dll -#22 4.172 libbar -> /source/libbar/bin/Debug/net9.0/libbar.dll -#22 4.620 tests -> /source/tests/bin/Debug/net9.0/tests.dll -#22 4.642 Test run for /source/tests/bin/Debug/net9.0/tests.dll (.NETCoreApp,Version=v9.0) -#22 4.745 VSTest version 17.12.0-preview-24412-03 (x64) -#22 4.751 -#22 4.858 Starting test execution, please wait... -#22 4.893 A total of 1 test files matched the specified pattern. -#22 5.615 Results File: /source/tests/TestResults/_buildkitsandbox_2024-09-05_17_14_28.trx -#22 5.616 -#22 5.620 Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3, Duration: 7 ms - tests.dll (net9.0) -#22 DONE 5.7s - -... -``` - -There are two main limitations to this approach: - -* It's not possible to get the test logs out of the stage that was used to run the tests during the build. -* If tests fail, the image fails to build. This leaves you without a container image that you could otherwise use to further diagnose the problem that caused the tests to fail. +There are many ways to run automated tests against your projects. +[Running Tests with Docker](../run-tests-in-sdk-container.md) is one example that covers running this sample's tests using the separate executable test stage in the [`complexapp` Dockerfile](./Dockerfile). ## More Samples diff --git a/samples/run-tests-in-sdk-container.md b/samples/run-tests-in-sdk-container.md index 1f2b01e562..4eb9c00320 100644 --- a/samples/run-tests-in-sdk-container.md +++ b/samples/run-tests-in-sdk-container.md @@ -1,84 +1,72 @@ # Running Tests with Docker -You can use Docker to run your unit tests in an isolated environment using the [.NET SDK Docker image](../README.sdk.md). This is useful if your development and production environments don't match, like, for example, Windows and Linux, respectively. There are multiple ways to run unit tests in containers, which are demonstrated in this document. +You can use Docker to run your unit tests in a controlled environment using the [.NET SDK Docker image](../README.sdk.md). +Running tests in a container has several benefits: -[Building in an SDK container](build-in-sdk-container.md) is a similar scenario and relies on similar patterns. [Building and testing multiple projects with Docker](complexapp/README.md) sample offers additional test patterns that you may want to adopt. +- Tests are more insulated from dev and build machine configuration. +- Tests can run in an environment that more closely matches production - this is especially useful if your development and production environments don't match. +- Any additional setup needed for running tests can be configured in a Dockerfile. -This document uses the [tests](complexapp/tests) that are part of [complexapp](complexapp). The instructions assume that you are in the [complexapp](complexapp) directory. +[Building in an SDK container](./build-in-sdk-container.md) is a similar scenario and relies on similar patterns. -The following examples demonstrate using `dotnet test` in a .NET SDK container. It builds tests and dependent projects from source and then runs them. You have to re-launch the container every time you want to test source code changes. +The following is just one example of how to test .NET apps with Docker and isn't intended to be comprehensive. +There are multiple ways to run unit tests in containers, most of which which aren't .NET-specific. +A good approach to automated testing is to make sure that tests run in an environment similar to production, make sure that failing tests fail the build/test/deployment pipeline that they run in, and make sure to collect detailed test output/logs regardless of test success or failure. -Alternatively, you can use `dotnet watch test`. This command reruns tests within a running container with every local code change. +This example uses the [tests](./complexapp/tests) that are part of [complexapp](./complexapp). +The instructions assume that you have cloned this repo and are in the [complexapp](./complexapp) directory. -## Requirements +## Running tests in an executable stage -The instructions assume that you have cloned the [repository](https://github.com/dotnet/dotnet-docker) locally. +The [complexapp Dockerfile](./complexapp/Dockerfile) includes a `test` stage that demonstrates running via its `ENTRYPOINT`, as follows: -You may need to enable [shared drives (Windows)](https://docs.docker.com/docker-for-windows/#shared-drives) or [file sharing (macOS)](https://docs.docker.com/docker-for-mac/#file-sharing) first. +```Dockerfile +# test exposes tests as the default executable for the stage +FROM test-build AS test +ENTRYPOINT ["dotnet", "test", "--no-build", "--logger:trx"] +``` -Container scenarios that use volume mounting can produce conflicts between the `bin` and `obj` directories in local and container environments. To avoid that, you need to use a different set of `obj` and `bin` folders for your container environment. The easiest way to do that is to copy a custom [Directory.Build.props](Directory.Build.props) into the directory you are using (like the `complexapp` directory in the following example), either via copying from this repo or downloading with the following command: +The `test` stage can be built directly using the `--target` argument: -```console -curl -o Directory.Build.props https://raw.githubusercontent.com/dotnet/dotnet-docker/main/samples/Directory.Build.props +```pwsh +# Build the test image +PS> docker build --pull --target test -t complexapp-tests . + => [internal] load build definition from Dockerfile + ... + => => naming to docker.io/library/complexapp-tests:latest + => => unpacking to docker.io/library/complexapp-tests:latest ``` -> [!NOTE] -> You may need to remove `bin` and `obj` directories if you run these instructions on Windows in both Windows and Linux container modes. +Then, the `complexapp-tests` image can be run with the test results output directory mounted into the running container: -## Running tests +```pwsh +# Create output directory for test results +PS> mkdir TestResults -You can run `dotnet test` within a .NET SDK container using the following pattern, with `docker run` and volume mounting. This initial example is demonstrated on Windows with PowerShell (in Linux container mode). Instructions for all OSes follow. -```console -> docker run --rm -v ${pwd}:/app -w /app/tests mcr.microsoft.com/dotnet/sdk:8.0 dotnet test - Determining projects to restore... - Restored /app/libbar/libbar.csproj (in 251 ms). - Restored /app/libfoo/libfoo.csproj (in 250 ms). - Restored /app/tests/tests.csproj (in 4.58 sec). - libbar -> /app/libbar/bin/Debug/net8.0/libbar.dll - libfoo -> /app/libfoo/bin/Debug/net8.0/libfoo.dll - tests -> /app/tests/bin/Debug/net8.0/tests.dll -Test run for /app/tests/bin/Debug/net8.0/tests.dll (.NETCoreApp,Version=v8.0) -Microsoft (R) Test Execution Command Line Tool Version 17.8.0 (x64) -Copyright (c) Microsoft Corporation. All rights reserved. +# Run the test image, mounting the TestResults directory into the container +PS> docker run --rm -v ${pwd}/TestResults:/source/tests/TestResults complexapp-tests +Test run for /source/tests/bin/Debug/net9.0/tests.dll (.NETCoreApp,Version=v9.0) +VSTest version 17.12.0-preview-24412-03 (x64) Starting test execution, please wait... A total of 1 test files matched the specified pattern. +Results File: /source/tests/TestResults/_51029443fea7_2024-09-27_16_25_14.trx -Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3, Duration: 3 ms - tests.dll (net8.0) -``` - -In this example, the tests (and any other required code) are [volume mounted](https://docs.docker.com/engine/admin/volumes/volumes/) into the container, and `dotnet test` is run from the `tests` directory (`-w` sets the working directory). Test results can be read from the console or from logs, which can be written to disk with the `--logger:trx` flag. - -When the `--logger:trx` flag is used, you should find a `.trx` file in the TestResults folder. You can open this file in Visual Studio to see the results of the test run, as you can see in the following image. You can open it in Visual Studio (File -> Open -> File) or double-click on the TRX file (if you have Visual Studio installed). There are other TRX file viewers available as well, which you can search for. - -![Visual Studio Test Results](https://user-images.githubusercontent.com/2608468/35361940-2f5ab914-0118-11e8-9c40-4f252f4568f0.png) - -The following instructions demonstrate this scenario in various configurations with logging enabled. +Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3, Duration: 9 ms - tests.dll (net9.0) -### Linux or macOS - -```console -docker run --rm -v $(pwd):/app -w /app/tests mcr.microsoft.com/dotnet/sdk:8.0 dotnet test --logger:trx -``` - -### Windows using Linux containers - -This example uses PowerShell. - -```console -docker run --rm -v ${pwd}:/app -w /app/tests mcr.microsoft.com/dotnet/sdk:8.0 dotnet test --logger:trx -``` -### Windows using Windows containers +# View the test results on the host machine +PS> ls TestResults -This example uses PowerShell. + Directory: C:\s\dotnet-docker\samples\complexapp\TestResults -```console -docker run --rm -v ${pwd}:C:\app -w C:\app\tests mcr.microsoft.com/dotnet/sdk:8.0-nanoserver-ltsc2022 dotnet test --logger:trx +Mode LastWriteTime Length Name +---- ------------- ------ ---- +-a--- 9/27/2024 9:25 AM 4962 _51029443fea7_2024-09-27_16_25_14.trx ``` ## More Samples -* [.NET Docker Samples](../README.md) -* [.NET Framework Docker Samples](https://github.com/microsoft/dotnet-framework-docker-samples/) +- [.NET Docker Samples](../README.md) +- [.NET Framework Docker Samples](https://github.com/microsoft/dotnet-framework-docker-samples/)