Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite complexapp test documentation #5926

Merged
merged 7 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions samples/complexapp/Dockerfile
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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"]
143 changes: 4 additions & 139 deletions samples/complexapp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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

Expand Down
100 changes: 44 additions & 56 deletions samples/run-tests-in-sdk-container.md
Original file line number Diff line number Diff line change
@@ -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/)