为什么 Pester 不使用陷阱捕获错误

Why doesn't Pester catch errors using a trap

我想知道为什么在 运行 运行此脚本时出现以下行为。我在 PowerShell ISE(v4 主机)中加载了脚本并加载了 Pester 模块。我按 F5 运行 脚本。

function Test-Pester {
  throw("An error")
}

Describe "what happens when a function throws an error" {

  Context "we test with Should Throw" {

    It "Throws an error" {
      { Test-Pester } | Should Throw
    }
  }

  Context "we test using a try-catch construct" {

    $ErrorSeen = $false
    try {
      Test-Pester
    }
    catch {
      $ErrorSeen = $true
    }

    It "is handled by try-catch" {
      $ErrorSeen | Should Be $true
    }
  }

  Context "we test using trap" {

    trap {
      $ErrorSeen = $true
    }

    $ErrorSeen = $false

    Test-Pester

    It "is handled by trap" {
      $ErrorSeen | Should Be $true
    }
  }
}

然后我得到以下输出:

Describing what happens when a function throws an error
   Context we test with Should Throw
    [+] Throws an error 536ms
   Context we test using a try-catch construct
    [+] is handled by try-catch 246ms
   Context we test using trap
An error
At C:\Test-Pester.ps1:2 char:7
+       throw("An error")
+       ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (An error:String) [], RuntimeException
    + FullyQualifiedErrorId : An error

    [-] is handled by trap 702ms
      Expected: {True}
      But was:  {False}
      at line: 40 in C:\Test-Pester.ps1
      40:           $ErrorSeen | Should Be $true

问题

为什么 trap{} 显然没有 运行ning 在期末考试中?

根据this blog,你需要告诉你的陷阱对控制流做一些事情:

The [...] thing you notice is that when you run this as script, you will receive both your error message and the red PowerShell error message.

. 'C:\Scripts\test.ps1'
Something terrible happened!
Attempted to divide by zero.
At C:\Scripts\test.ps1:2 Char:3
+ 1/ <<<< null

This is because your Trap did not really handle the exception. To handle an exception, you need to add the "Continue" statement to your trap:

trap { 'Something terrible happened!'; continue }
1/$null

Now, the trap works as expected. It does whatever you specified in the trap script block, and PowerShell does not get to see the exception anymore. You no longer get the red error message.

根据@PetSerAl 和@Eris 的 comments/suggested 回答,这里有两个问题解决方案。我也已经阅读并感谢对这个问题的回答:

Why are variable assignments within a Trap block not visible outside it?

解决方案 1

虽然脚本中设置的变量可以在陷阱中读取,但无论您在陷阱中做什么,都会发生该变量的 copy,即仅在本地范围内到陷阱。在此解决方案中,我们将 reference 评估为 $ErrorSeen 以便当我们设置变量的值时,我们实际上是在设置父作用域中存在的变量的值。

向陷阱添加 continue 会抑制 ErrorRecord 详细信息,清理测试的输出。

Describe "what happens when a function throws an error" {
  Context "we test using trap" {

    $ErrorSeen = $false

    trap {
      Write-Warning "Error trapped"
      ([Ref]$ErrorSeen).Value = $true
      continue
    }

    Test-Pester

    It "is handled by trap" {
      $ErrorSeen | Should Be $true
    }
  }
}

解决方案 2

按照与第一个解决方案相同的思路,可以通过显式设置 $ErrorSeen 变量的范围来解决问题(1)首次创建时和(2)在 [= 中使用时15=]。我在这里使用了 Script 作用域,但 Global 似乎也可以。

同样的原则也适用于此:我们需要避免陷阱中变量的更改仅发生在变量的本地副本上的问题。

Describe "what happens when a function throws an error" {
  Context "we test using trap" {

    $Script:ErrorSeen = $false

    trap {
      Write-Warning "Error trapped"
      $Script:ErrorSeen = $true
      continue
    }

    Test-Pester

    It "is handled by trap" {
      $ErrorSeen | Should Be $true
    }
  }
}