困扰 5:自动化

Pester 5: Automate It

为了我的爱好项目ConvertTo-Expression, I am rebuilding my test (Pester 5) script. I would like to automate the It (and possibly the Context part) as there are large number of syntax formats to test for and the function actually roundtrips which &([ScriptBlock]::Create("$Expression")). For a Minimal, Reproducible Example, I am using ConvertTo-Json which roundtrips with ConvertTo-Json
我对这个问题的目标基本上是创建一个 easy 测试语法相关函数是否正确往返,例如:

Test -Compress '{"a":1}'

我想做这样的事情:

Function Test ([String]$Json, [Switch]$Compress, [String]$It = $Json) {
    $Context = if ($Compress) { 'Compress' } Else { 'Default' }
    $Object = ConvertFrom-Json $Json
    Context $Context {
#       BeforeEach {
#           $Compress = $True
#           $Json = '{"a":1}'
#           $Object = ConvertFrom-Json $Json
#       }
        It $It { ConvertTo-Json -Compress:$Compress -Depth 9 $Object | Should -Be $Json }
    }
}

Describe 'Syntax check' {

    Test -Compress '{"a":1}'

}

但这会导致如下错误:

Starting discovery in 1 files.
Discovery finished in 35ms.
[-] Syntax check.Compress.{"a":1} 15ms (13ms|2ms)
 RuntimeException: The variable '$Compress' cannot be retrieved because it has not been set.
 at <ScriptBlock>, C:\Test.ps1:6
Tests completed in 140ms
Tests Passed: 0, Failed: 1, Skipped: 0 NotRun: 0

启用注释掉的硬编码 BeforeEach returns expcted 结果:

Starting discovery in 1 files.
Discovery finished in 32ms.
[+] C:\Test.ps1 197ms (80ms|88ms)
Tests completed in 202ms
Tests Passed: 1, Failed: 0, Skipped: 0 NotRun: 0

我想把It(和Context)放在一个函数中并用参数控制它们,这样我就可以做一个像Test -Compress '{"a":1}'这样的简单测试。不幸的是,我完全迷失在新的 Pester 发现范围中,并开始怀疑这是否真的可以通过 Pester 5 实现。

我找到了一种使用 -TestCases 参数的方法:

Function Test ([String]$Json, [Switch]$Compress, [String]$It = $Json) {
    $Context = if ($Compress) { 'Compress' } Else { 'Default' }
    $Object = ConvertFrom-Json $Json
    Context $Context {
        $TestCase = @{ Params = @{ Compress = $Compress; Object = $Object; Json = $Json } }
        It $It -TestCases $TestCase { 
            param($Params)
            ConvertTo-Json -Compress:$Params.Compress -Depth 9 $Params.Object | Should -Be $Params.Json
        }
    }
}

Describe 'Syntax check' {

    Test -Compress '{"a":1}'

}

(但我仍然愿意接受其他建议,尤其是如果我似乎忽略了一些明显的东西)

我认为问题在于感兴趣的变量仅在 嵌套 脚本块(传递给 It 的脚本块)内被引用,而不是外部(一个传递给 Context).

一个简单的解决方案是在外部脚本块上调用 .GetNewClosure() 方法,它在调用作用域的局部变量周围形成一个闭包:

Function Test ([String]$Json, [Switch]$Compress, [String]$It = $Json) {
    $Context = if ($Compress) { 'Compress' } Else { 'Default' }
    $Object = ConvertFrom-Json $Json
    Context $Context {
        It $It { ConvertTo-Json -Compress:$Compress -Depth 9 $Object | Should -Be $Json }
    }.GetNewClosure() # This captures $Json, $Compress, $It, and $Object
}

Describe 'Syntax check' {
    Test -Compress '{"a":1}'
}

请注意,docs are very terse at the moment, but the conversation in GitHub issue #9077 表明 .GetNewClosure() 返回的脚本块在新创建的 动态(内存中)模块中运行