Pester 如何模拟模式 "test existence (not found) - create - test again to confirm creation" 中的 "test" 函数?
How can Pester mock the "test" function in the pattern "test existence (not found) - create - test again to confirm creation"?
下面是一些伪代码,显示了要测试的函数:
function Set-Something
{
if (Test-Something)
{
return $True
}
# Not found so do something to create it.
Do-Something
# Check it's been created successfully.
if (Test-Something)
{
return $True
}
return $False
}
这一定是一个相当常见的模式:"Test for existence - if not found create - test again to verify creation"。测试大多数分支非常简单,但是如何测试第一次调用 Test-Something 失败,然后第二次调用成功的分支?
到目前为止,这是我的测试代码:
Describe 'Set-Something' {
Context 'already exists' {
Mock Test-Something { return $True }
Mock Do-Something
It 'returns True' {
{ Set-Something } | Should -Be $True
}
It 'does not call Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 0 -Exactly
}
}
Context 'does not already exist and creation fails' {
Mock Test-Something { return $False }
Mock Do-Something
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns False' {
{ Set-Something } | Should -Be $False
}
}
Context 'does not already exist and creation succeeds' {
Mock Test-Something { ?? }
Mock Do-Something
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns True' {
{ Set-Something } | Should -Be $True
}
}
}
案例'does not already exist and creation succeeds'就是问题所在。 Test-Something 需要被模拟,所以它在第一次被调用时失败,第二次成功。传递给 Test-Something 的参数在每次调用中都是相同的,所以我不能使用 ParameterFilter 创建两个具有不同行为的 Test-Something 模拟。
我找到了几种模拟这个的方法:
1)使用一个"static"(即script-scoped)变量来记录状态
Context 'does not already exist and creation succeeds' {
BeforeEach {
$script:exists = $False
}
AfterAll {
Remove-Variable exists -Scope Script
}
Mock Test-Something {
return $script:exists
}
Mock Do-Something {
$script:exists = $True
}
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns True' {
{ Set-Something } | Should -Be $True
}
}
2) 使用散列table记录状态
Context 'does not already exist and creation succeeds' {
BeforeEach {
$mockState = @{
ItExists = $False
}
}
Mock Test-Something {
return $mockState.ItExists
}
Mock Do-Something {
$mockState.ItExists = $True
}
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns True' {
{ Set-Something } | Should -Be $True
}
}
就个人而言,我喜欢散列 table,因为在我看来 $mockState. ...
比 $script:...
更能表明变量的用途。此外,如果测试曾被并行化并且另一个 Describe 块修改了相同的变量,script-scoped 变量可能会导致竞争条件。
下面是一些伪代码,显示了要测试的函数:
function Set-Something
{
if (Test-Something)
{
return $True
}
# Not found so do something to create it.
Do-Something
# Check it's been created successfully.
if (Test-Something)
{
return $True
}
return $False
}
这一定是一个相当常见的模式:"Test for existence - if not found create - test again to verify creation"。测试大多数分支非常简单,但是如何测试第一次调用 Test-Something 失败,然后第二次调用成功的分支?
到目前为止,这是我的测试代码:
Describe 'Set-Something' {
Context 'already exists' {
Mock Test-Something { return $True }
Mock Do-Something
It 'returns True' {
{ Set-Something } | Should -Be $True
}
It 'does not call Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 0 -Exactly
}
}
Context 'does not already exist and creation fails' {
Mock Test-Something { return $False }
Mock Do-Something
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns False' {
{ Set-Something } | Should -Be $False
}
}
Context 'does not already exist and creation succeeds' {
Mock Test-Something { ?? }
Mock Do-Something
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns True' {
{ Set-Something } | Should -Be $True
}
}
}
案例'does not already exist and creation succeeds'就是问题所在。 Test-Something 需要被模拟,所以它在第一次被调用时失败,第二次成功。传递给 Test-Something 的参数在每次调用中都是相同的,所以我不能使用 ParameterFilter 创建两个具有不同行为的 Test-Something 模拟。
我找到了几种模拟这个的方法:
1)使用一个"static"(即script-scoped)变量来记录状态
Context 'does not already exist and creation succeeds' {
BeforeEach {
$script:exists = $False
}
AfterAll {
Remove-Variable exists -Scope Script
}
Mock Test-Something {
return $script:exists
}
Mock Do-Something {
$script:exists = $True
}
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns True' {
{ Set-Something } | Should -Be $True
}
}
2) 使用散列table记录状态
Context 'does not already exist and creation succeeds' {
BeforeEach {
$mockState = @{
ItExists = $False
}
}
Mock Test-Something {
return $mockState.ItExists
}
Mock Do-Something {
$mockState.ItExists = $True
}
It 'calls Do-Something' {
Set-Something
Assert-MockCalled Do-Something -Times 1 -Exactly
}
It 'calls Test-Something twice' {
Set-Something
Assert-MockCalled Test-Something -Times 2 -Exactly
}
It 'returns True' {
{ Set-Something } | Should -Be $True
}
}
就个人而言,我喜欢散列 table,因为在我看来 $mockState. ...
比 $script:...
更能表明变量的用途。此外,如果测试曾被并行化并且另一个 Describe 块修改了相同的变量,script-scoped 变量可能会导致竞争条件。