2

我想知道为什么在运行此脚本时会出现以下行为。我在 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{}最终测试中显然没有运行?

4

2 回答 2

2

以下是基于@PetSerAl 和@Eris 的评论/建议答案的问题的两种解决方案。我也已阅读并赞赏对这个问题的回答:

为什么 Trap 块内的变量赋值在其外部不可见?

解决方案 1

尽管可以在陷阱中读取脚本中设置的变量,但无论您在陷阱中做什么,都会发生该变量的副本,即仅在陷阱的本地范围内。在这个解决方案中,我们评估一个引用$ErrorSeen这样当我们设置变量的值时,我们实际上是在设置存在于父作用域中的变量的值。

向陷阱添加 acontinue会抑制 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) 在trap {}. 我在这里使用了 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
    }
  }
}
于 2016-04-16T20:28:00.643 回答
1

根据这个博客,你需要告诉你的陷阱对控制流做一些事情:

[...] 您注意到的是,当您将其作为脚本运行时,您将同时收到错误消息和红色的 PowerShell 错误消息。

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

这是因为您的 Trap 并没有真正处理异常。要处理异常,您需要在陷阱中添加“继续”语句:

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

现在,陷阱按预期工作。它执行您在陷阱脚本块中指定的任何操作,并且 PowerShell 不再看到异常。您不再收到红色错误消息。

于 2016-04-15T17:35:22.713 回答