Classic Azure Pipeline中的Pester为什么总是失败?

huangapple go评论63阅读模式
英文:

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

Classic Azure Pipeline中的Pester为什么总是失败?

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

Classic Azure Pipeline中的Pester为什么总是失败?

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"

我的教训:

  1. 将问题分解得更小。将问题减少到一个单一测试,直接显示一个测试是问题的原因,即使它们都通过了。
  2. 在Azure DevOps中,无法使用Pester测试以下行,因为它将始终使您的测试失败:Write-Host "##vso[task.complete result=Failed;]DONE"
  3. 执行该行的测试将显示“Passed”,这可能是正确的,但令人讨厌且难以找到。
  4. 您不能在之后通过以下行覆盖它: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

  1. 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.
  2. 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"
  3. A test executing that line, will show "Passed" which can be correct, but is annoying and hard to find.
  4. 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.

huangapple
  • 本文由 发表于 2023年4月13日 17:28:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76003846.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定