Docker构建中.NET测试失败时,如何将测试报告复制到Agent?
问题场景
我通过Docker运行基于Specflow+Runner的C#自动化测试用例,借助Azure DevOps的master.yml流水线文件,使用共享Agent构建Docker镜像。当前Dockerfile里写了四种测试执行方式,但核心需求是:即使Dockerfile中的测试用例执行失败,也要能把测试报告复制到Agent机器上。
现有配置文件如下:
原master.yml
jobs: - job: run_docker_build displayName: Run UI Build steps: #------------------------------------------ # Build Docker #------------------------------------------ - task: Docker@2 displayName: DockerBuild env: COMPOSE_DOCKER_CLI_BUILD: 1 DOCKER_BUILDKIT: 1 inputs: containerRegistry: "XXX" repository: run-automation-ui command: build Dockerfile: '**/Dockerfile' tags: | latest $(Build.BuildId) #------------------------------------------ # Copy the Results from Build Image #------------------------------------------ - pwsh: | $id=docker images --filter "label=Test=True" -q | Select-Object -First 1 docker create --name testcontainer $id docker cp testcontainer:/src/AutomationTests/TestResults ./TestResults docker cp testcontainer:/src/AutomationTests/TestResults/Screenshots ./Screenshots docker rm testcontainer displayName: 'Copy Test Results' condition: always() #------------------------------------------ # Publishing Test Results #------------------------------------------ - task: PublishTestResults@2 inputs: testResultsFormat: 'VSTest' testResultsFiles: '**/*.trx' searchFolder: '$(System.DefaultWorkingDirectory)/TestResults' publishRunAttachments: true displayName: 'Publish Test Results' condition: always()
原Dockerfile
LABEL Test=True ##Method one (#Even if test fails the build task will finish and in later stage I can copy the Results ) #RUN dotnet test -c Release --no-build --logger "trx;LogFileName=/src/AutomationTests/TestResults/test_results.trx" --filter TestCategory=DockerTest || true #Method Two (#Even if test fails the build task will finish and in later stage I can copy the Results ) #RUN dotnet test -c Release --no-build --logger "trx;LogFileName=/src/AutomationTests/TestResults/test_results.trx" --filter TestCategory=DockerTest; exit 0 #Method Three (#Even if test fails the build task will finish and in later stage I can copy the Results ) #RUN dotnet test -c Release --no-build --logger "trx;LogFileName=/src/AutomationTests/TestResults/test_results.trx" --filter TestCategory=DockerTest; \ #echo $? > /dotnet.exitcode; #RUN exit $(cat /dotnet.exitcode) #Method Four (if test fails means still i need to able to copy the results ) #RUN dotnet test -c Release --no-build --logger "trx;LogFileName=/src/AutomationTests/TestResults/test_results.trx" --filter TestCategory=DockerTest
解决方案
核心问题是:Docker构建过程中,如果RUN指令返回非0退出码,构建会直接中断,导致后续复制报告的步骤根本无法触发。所以需要做到两点:
- 让Docker构建即使测试失败也能完成,确保测试报告被保留在镜像中
- 保留测试失败的状态,让流水线最终能反映真实的测试结果
1. 调整Dockerfile的测试执行逻辑
推荐使用改进后的方法三,既保证构建完成,又保留测试的真实退出码:
LABEL Test=True # 执行测试,保存退出码到文件,确保构建不中断 RUN dotnet test -c Release --no-build --logger "trx;LogFileName=/src/AutomationTests/TestResults/test_results.trx" --filter TestCategory=DockerTest; \ echo $? > /dotnet.exitcode # 不直接退出,让构建完成,后续流水线读取退出码判断结果
为什么不用其他方法?
- 方法一/二:用
|| true或exit 0强制让RUN指令返回0,会掩盖测试失败的真实状态,流水线会误判为构建成功 - 方法四:测试失败直接中断构建,镜像构建不完成,根本拿不到测试报告
2. 优化流水线步骤(master.yml)
在复制测试报告后,读取Docker镜像中保存的退出码,让流水线最终状态匹配测试结果:
jobs: - job: run_docker_build displayName: Run UI Build steps: #------------------------------------------ # Build Docker #------------------------------------------ - task: Docker@2 displayName: DockerBuild env: COMPOSE_DOCKER_CLI_BUILD: 1 DOCKER_BUILDKIT: 1 inputs: containerRegistry: "XXX" repository: run-automation-ui command: build Dockerfile: '**/Dockerfile' tags: | latest $(Build.BuildId) # 允许构建完成(Dockerfile里不会中断),后续步骤判断测试结果 continueOnError: true #------------------------------------------ # Copy the Results from Build Image #------------------------------------------ - pwsh: | $id=docker images --filter "label=Test=True" -q | Select-Object -First 1 if (-not $id) { Write-Error "No test image found" exit 1 } docker create --name testcontainer $id # 复制测试报告 docker cp testcontainer:/src/AutomationTests/TestResults ./TestResults docker cp testcontainer:/src/AutomationTests/TestResults/Screenshots ./Screenshots # 读取测试退出码 $exitCode = docker exec testcontainer cat /dotnet.exitcode docker rm testcontainer # 将退出码传递给后续步骤 Write-Host "##vso[task.setvariable variable=TestExitCode;]$exitCode" displayName: 'Copy Test Results' condition: always() #------------------------------------------ # 根据测试退出码设置流水线状态 #------------------------------------------ - pwsh: | if ($env:TestExitCode -ne 0) { Write-Error "Automation tests failed with exit code $env:TestExitCode" exit 1 } displayName: 'Check Test Result' condition: always() #------------------------------------------ # Publishing Test Results #------------------------------------------ - task: PublishTestResults@2 inputs: testResultsFormat: 'VSTest' testResultsFiles: '**/*.trx' searchFolder: '$(System.DefaultWorkingDirectory)/TestResults' publishRunAttachments: true displayName: 'Publish Test Results' condition: always()
关键优化点:
- 在Docker Build步骤添加
continueOnError: true,确保即使测试失败(Dockerfile里的退出码会被保存,但构建本身完成),流水线能继续执行后续步骤 - 复制报告时读取镜像中的
/dotnet.exitcode文件,将其设置为流水线变量 - 添加专门的步骤判断测试退出码,如果非0则让流水线失败,保证最终状态真实反映测试结果
- 所有涉及报告复制和发布的步骤都保留
condition: always(),确保无论前面步骤如何都执行
内容的提问来源于stack exchange,提问作者srikanth reddy karumuri
相关产品推荐
相关产品推荐

