I am new to Docker, and I have the Dockerfile below which Azure Pipelines is using to build an image and push it to an Azure Container Registry.
// Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["poc.frontend/poc.frontend.csproj", "poc.frontend/"]
RUN dotnet restore "poc.frontend/poc.frontend.csproj"
COPY ["Tests/poc.frontend/poc.frontend.unittests/poc.frontend.unittests.csproj", "poc.tests/"]
RUN dotnet restore "poc.tests/poc.frontend.unittests.csproj"
COPY . .
WORKDIR "/src/poc.frontend"
RUN dotnet build "poc.frontend.csproj" -c Release
FROM build AS test
LABEL test=true
WORKDIR "/src/poc.tests"
RUN dotnet build "poc.frontend.unittests.csproj" -c Release
RUN dotnet test --test-adapter-path "bin/Release/net6.0/NUnit3.TestAdapter.dll" -c Release --results-directory /testresults --logger "trx;LogFileName=test_results.trx" "poc.frontend.unittests.csproj"
FROM build AS publish
WORKDIR "/src/poc.frontend"
RUN dotnet publish "poc.frontend.csproj" -c Release -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "poc.frontend.dll"]
When the build pipeline uses this Dockerfile as part of the Docker@2 task, it skips the test part. After building the application it goes immediately to the publish part.
// build-template.yaml
// ... other tasks
- task: Docker@2
displayName: 'Build and push an image to ACR'
inputs:
command: buildAndPush
repository: ${{parameters.imageRepository}}
dockerFile: ${{parameters.dockerFile}}
containerRegistry: sc-acr-sp
buildContext: $(Build.SourcesDirectory)
tags: v$(Build.BuildId)
These are what the logs are showing.
...
#17 [build 9/9] RUN dotnet build "poc.frontend.csproj" -c Release
#17 0.285 MSBuild version 17.3.2+561848881 for .NET
#17 1.000 Determining projects to restore...
#17 1.398 All projects are up-to-date for restore.
##[debug]Agent environment resources - Disk: / Available 18735.00 MB out of 74244.00 MB, Memory: Used 883.00 MB out of 6932.00 MB, CPU: Usage 15.91%
#17 3.825 /src/poc.frontend/Repositories/Mock/MockBasketRepository.cs(15,35): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [/src/poc.frontend/poc.frontend.csproj]
#17 3.826 /src/poc.frontend/Repositories/Mock/MockBasketRepository.cs(22,35): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [/src/poc.frontend/poc.frontend.csproj]
#17 3.939 poc.frontend -> /src/poc.frontend/bin/Release/net6.0/poc.frontend.dll
#17 3.956
#17 3.956 Build succeeded.
#17 3.957
#17 3.957 /src/poc.frontend/Repositories/Mock/MockBasketRepository.cs(15,35): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [/src/poc.frontend/poc.frontend.csproj]
#17 3.957 /src/poc.frontend/Repositories/Mock/MockBasketRepository.cs(22,35): warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. [/src/poc.frontend/poc.frontend.csproj]
#17 3.957 2 Warning(s)
#17 3.957 0 Error(s)
#17 3.957
#17 3.957 Time Elapsed 00:00:03.53
#17 DONE 4.0s
#18 [publish 1/2] WORKDIR /src/poc.frontend
#18 DONE 0.0s
...
What should I do so that the test stage doesn't get ignored?
On the Docker Engine earlier than version 23.0, the legacy Docker Engine builder processes all stages of a Dockerfile leading up to the selected --target
. It will build a stage even if the selected target doesn't depend on that stage.
However, since Docker Engine 23.0, BuildKit is the default builder for users. BuildKit only builds the stages that the target stage depends on.
For your case, with BuildKit enabled (DOCKER_BUILDKIT=1):
final
) depends on 'base
' (FROM base AS final
) and 'publish
' (COPY --from=publish /app/publish .
) stages.publish
' depends on 'build
' stage (FROM build AS publish
). So, the target stage also is determined to depend on 'build
' stage.test
' stage is not depended on by the target stage, and the other stages depended on by the target stage also does not depend on the 'test
' stage.So, finally, only the 'base
', 'build
', 'publish
' and 'final
' stages are executed when building the Dockerfile.
To disable BuildKit, you can use one of the following methods:
Set the pipeline variable below. It will map/override the environment variable with the new value "DOCKER_BUILDKIT=0".
variables:
DOCKER_BUILDKIT: 0
Directly add "DOCKER_BUILDKIT=0" before the "docker build
" command. It will build Dockerfile with BuildKit disabled.
DOCKER_BUILDKIT=0 docker build xxx
Related documentations:
EDIT:
If you do not want to disable BuildKit, you can try to set up a job in pipeline to specifically run the tests and publish the test results outside of the Dockerfile
:
Use the default checkout task to check out the code files of your C# projects.
Use the DotNetCoreCLI task to run the related dotnet
commands, such as dotnet build
, dotnet test
, etc...
- task: DotNetCoreCLI@2
displayName: 'dotnet test'
inputs:
command: test
projects: poc.frontend.unittests.csproj
arguments: '-c Release --test-adapter-path "bin/Release/net6.0/NUnit3.TestAdapter.dll" --results-directory "./testresults" --logger "trx;logfilename=test_results.trx"'
publishTestResults: false
You also can use a script task (such as Bash, PowerShell, CmdLine, etc..) to execute the related commands.
- task: Bash@3
displayName: 'dotnet test'
inputs:
targetType: inline
script: |
dotnet test "poc.frontend.unittests.csproj" \
-c Release \
--test-adapter-path "bin/Release/net6.0/NUnit3.TestAdapter.dll" \
--results-directory "./testresults" \
--logger "trx;logfilename=test_results.trx"
Use the PublishTestResults task to publish the test results to Azure Pipelines.
Below is a sample of the test job.
jobs:
- job: test
displayName: 'Run unittests'
steps:
- task: DotNetCoreCLI@2
displayName: 'dotnet test'
inputs:
command: test
projects: poc.frontend.unittests.csproj
arguments: '-c Release --test-adapter-path "bin/Release/net6.0/NUnit3.TestAdapter.dll" --results-directory "./testresults" --logger "trx;logfilename=test_results.trx"'
publishTestResults: false
- task: PublishTestResults@2
displayName: 'Publish Test Results'
inputs:
testResultsFormat: VSTest
testResultsFiles: '**/*.trx'
searchFolder: '$(System.DefaultWorkingDirectory)/testresults'