英文:
Why does Pester in Classic Azure Pipeline always fail?
问题
I have a classic build pipeline (not yaml, yet!) with three tasks
- Install Pester -> works as expected
- Run Pester tests -> always fails!
- Publish test results -> works as expected
My script for "Run Pester tests"
Import-Module Pester -Force
$config = [PesterConfiguration]::Default
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = "NUnitXml"
$config.TestResult.OutputPath = "$env:SYSTEM_DEFAULTWORKINGDIRECTORY\TEST-results.xml"
$config.Run.Path = ".\my\path*"
$config.Run.PassThru = $true
$results = Invoke-Pester -Configuration $config
if($results.Result -eq "Passed")
{
Write-host "All tests passed"
Write-Host "##vso[task.complete result=Succeeded;]DONE"
exit 0
}
Sample output in Azure DevOps is
Tests completed in 11.43s
Tests Passed: 67, Failed: 0, Skipped: 0 NotRun: 0
All tests passed
Finishing: Run Pester Unit Tests
However, the task always fails
I know from robocopy
that Powershell Tasks in Azure will check $LASTEXITCODE
and that you are able to overwrite that via Write-Host "##vso[task.complete result=Succeeded;]DONE"
and exit 0
. Why is this not working here?
What else have I tried?
- Activate
SilentlyContinue
-> results in Warning - Activate
Continue on Error
-> results in Warning - Activate
Ignore $LASTEXITCODE
-> results in Failure - Activate
Fail on Standard Error
-> results in Failure
英文:
I have a classic build pipeline (not yaml, yet!) with three tasks
- Install Pester -> works as expected
- Run Pester tests -> always fails!
- Publish test results -> works as expected
My script for "Run Pester tests"
Import-Module Pester -Force
$config = [PesterConfiguration]::Default
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = "NUnitXml"
$config.TestResult.OutputPath = "$env:SYSTEM_DEFAULTWORKINGDIRECTORY\TEST-results.xml"
$config.Run.Path = ".\my\path*"
$config.Run.PassThru = $true
$results = Invoke-Pester -Configuration $config
if($results.Result -eq "Passed")
{
Write-host "All tests passed"
Write-Host "##vso[task.complete result=Succeeded;]DONE"
exit 0
}
Sample output in Azure DevOps is
Tests completed in 11.43s
Tests Passed: 67, Failed: 0, Skipped: 0 NotRun: 0
All tests passed
Finishing: Run Pester Unit Tests
However, the task always fails
I know from robocopy
that Powershell Tasks in Azure will check $LASTEXITCODE
and that you are able to overwrite that via Write-Host "##vso[task.complete result=Succeeded;]DONE"
and exit 0
. Why is this not working here?
What else have I tried?
-
Activate
SilentlyContinue
-> results in Warning -
Activate
Continue on Error
-> results in Warning -
Activate
Ignore $LASTEXITCODE
-> results in Failure -
Activate
Fail on Standard Error
-> results in Failure
答案1
得分: 0
Oh boy this was a tough one:
Shayki Abramczyk的评论使我能够追踪到问题。是否可能以某种方式奖励他?
问题的原因是一个单元测试,通过Write-Host "##vso[task.complete result=Failed;]DONE"
更改了Azure DevOps任务的输出。由于我的Powershell脚本应该在Azure DevOps中工作,所以它们应该执行此类操作。测试以Pester的方式命名得很好:it "Should print out error if build requirement not found"
。
我的教训:
- 将问题分解得更小。将问题减少到一个单一测试,直接显示一个测试是问题的原因,即使它们都通过了。
- 在Azure DevOps中,无法使用Pester测试以下行,因为它将始终使您的测试失败:
Write-Host "##vso[task.complete result=Failed;]DONE"
- 执行该行的测试将显示“Passed”,这可能是正确的,但令人讨厌且难以找到。
- 您不能在之后通过以下行覆盖它:
Write-Host "##vso[task.complete result=Succeeded;]DONE"
英文:
Oh boy this was a tough one:
The comment by Shayki Abramczyk enabled me to track it down. Is it possible to grant him the bounty somehow?
The cause was a unit test, that was changing the Azure DevOps task output via Write-Host "##vso[task.complete result=Failed;]DONE"
. Since my Powershell scripts are supposed to work in Azure DevOps, they should do things like this. The test was named nicely for Pester it "Should print out error if build requirement not found"
My learning here
- Make your issues smaller. Reducing this to a single test, showed me directly that one of my tests was the cause even though they all passed.
- You cannot test the following line in Azure DevOps with Pester, because if will always make your tests fail
Write-Host "##vso[task.complete result=Failed;]DONE"
- A test executing that line, will show "Passed" which can be correct, but is annoying and hard to find.
- You cannot overwrite that by the following line afterwards
Write-Host "##vso[task.complete result=Succeeded;]DONE"
答案2
得分: 0
以下是您要翻译的内容:
"The relevant documentation is "Azure DevOps / Azure Pipelines / Logging commands / Complete: Finish timeline""
##vso[task.complete]current operation
result
=
Succeeded
: 任务成功。SucceededWithIssues
: 任务遇到问题。构建将以部分成功的方式完成。Failed
: 构建将以失败的方式完成。(如果选择了“错误时继续”的控制选项,则构建将以部分成功的方式完成。)
这意味着在Azure DevOps中使用##vso[task.complete]
日志命令时,它直接控制了任务的结果。如果您的单元测试执行了一行将任务结果设置为Failed
的代码,那么即使所有测试都通过,任务也会失败。
为了防止这种情况发生,您可能需要考虑不同的测试代码行的方法。而不是在测试中实际运行Write-Host "##vso[task.complete result=Failed;]DONE"
行,您可以模拟Write-Host
函数,当使用该参数调用时,它只返回特定的值或消息。
这样,您可以测试您的脚本是否正确调用了Write-Host
并传递了预期的参数,而不会实际影响任务的结果。
测试中的模拟Write-Host
可以如下所示:
Describe "Test Write-Host" {
It "Should print out error if build requirement not found" {
# 模拟Write-Host,只返回消息而不打印它
Mock Write-Host { return $args[0] }
# 运行您的脚本或调用Write-Host传递失败消息的函数
$result = Run-YourScript-Here
# 检查Write-Host是否以预期的参数被调用
Assert-MockCalled Write-Host -ParameterFilter { $args[0] -eq "##vso[task.complete result=Failed;]DONE" }
}
}
这样,您可以验证您的脚本是否正确调用了Write-Host
并传递了失败消息,而不会导致任务失败。
您的脚本变为:
Import-Module Pester -Force
$config = [PesterConfiguration]::Default
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = "NUnitXml"
$config.TestResult.OutputPath = "$env:SYSTEM_DEFAULTWORKINGDIRECTORY\TEST-results.xml"
$config.Run.Path = ".\my\path*"
$config.Run.PassThru = $true
# 模拟Write-Host,只返回消息而不打印它
Mock Write-Host { return $args[0] }
$results = Invoke-Pester -Configuration $config
if($results.FailedCount -eq 0)
{
Write-host "所有测试都通过"
Write-Host "##vso[task.complete result=Succeeded;]DONE"
exit 0
}
这将为Invoke-Pester
运行的所有测试模拟Write-Host
。
如果您想为特定的测试模拟它,您需要在该测试的Describe
块内包含模拟。
英文:
The relevant documentation is "Azure DevOps / Azure Pipelines / Logging commands / Complete: Finish timeline"
> ## ##vso[task.complete]current operation
>
> result
=
>
> - Succeeded
: The task succeeded.
> - SucceededWithIssues
: The task ran into problems. The build will be completed as partially succeeded at best.
> - Failed
: The build will be completed as failed. (If the Control Options: Continue on error option is selected, the build will be completed as partially succeeded at best.)
That means when you use the ##vso[task.complete]
logging command in Azure DevOps, it directly controls the outcome of the task. If your unit test executes a line that sets the task result to Failed
, then the task will fail, even if all your tests pass.
To prevent this, you might want to consider a different approach to testing that line of code. Instead of actually running the Write-Host "##vso[task.complete result=Failed;]DONE"
line in your test, you could mock the Write-Host
function to just return a specific value or message when it is called with that argument.
That way, you can test that your script is correctly calling Write-Host
with the expected argument, without actually affecting the outcome of the task.
A mock Write-Host
in your test could look like:
Describe "Test Write-Host" {
It "Should print out error if build requirement not found" {
# Mock Write-Host to just return the message instead of printing it
Mock Write-Host { return $args[0] }
# Run your script or function that's supposed to call Write-Host with the failure message
$result = Run-YourScript-Here
# Check that Write-Host was called with the expected argument
Assert-MockCalled Write-Host -ParameterFilter { $args[0] -eq "##vso[task.complete result=Failed;]DONE" }
}
}
This way, you can verify that your script is correctly calling Write-Host
with the failure message, without actually causing the task to fail.
You script becomes:
Import-Module Pester -Force
$config = [PesterConfiguration]::Default
$config.TestResult.Enabled = $true
$config.TestResult.OutputFormat = "NUnitXml"
$config.TestResult.OutputPath = "$env:SYSTEM_DEFAULTWORKINGDIRECTORY\TEST-results.xml"
$config.Run.Path = ".\my\path*"
$config.Run.PassThru = $true
# Mock Write-Host to just return the message instead of printing it
Mock Write-Host { return $args[0] }
$results = Invoke-Pester -Configuration $config
if($results.FailedCount -eq 0)
{
Write-host "All tests passed"
Write-Host "##vso[task.complete result=Succeeded;]DONE"
exit 0
}
This will mock Write-Host
for all the tests run by Invoke-Pester
.
If you want to mock it for a specific test, you'll need to include the mock inside the Describe
block of that test.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论