如何模拟使用不同参数和不同结果调用两次的命令
How to mock a command called twice with different parameters and different results
我有一个要用 Pester 测试的 PowerShell 函数:
function Install-RequiredModule (
[string]$ModuleName,
[string]$RepositoryName,
[string]$ProxyUrl
)
{
# Errors from Install-Module are non-terminating. They won't be caught using
# try - catch. So check $Error instead.
# Clear errors so we know if one shows up it must have been due to Install-Module.
$Error.Clear()
# Want to fail silently, without displaying anything in console to scare the user,
# because it's valid for Install-Module to fail for a user behind a proxy server.
Install-Module -Name $ModuleName -Repository $RepositoryName `
-ErrorAction SilentlyContinue -WarningAction SilentlyContinue
if ($Error.Count -eq 0)
{
# throw 'NO error'
return
}
# There was an error so try again, this time with proxy details.
$proxyCredential = Get-Credential -Message 'Please enter credentials for proxy server'
# No need to Silently Continue this time. We want to see the error details.
$Error.Clear()
Install-Module -Name $ModuleName -Repository $RepositoryName `
-Proxy $ProxyUrl -ProxyCredential $proxyCredential
if ($Error.Count -gt 0)
{
throw $Error[0]
}
if (-not (Get-InstalledModule -Name $ModuleName -ErrorAction SilentlyContinue))
{
throw "Unknown error installing module '$ModuleName' from repository '$RepositoryName'."
}
Write-Output "Module '$ModuleName' successfully installed from repository '$RepositoryName'."
}
这个函数可以调用Install-Module两次。它首先在没有代理凭据的情况下尝试,就好像它可以直接访问互联网一样。如果失败,它会再次尝试,这次使用代理凭据。
如何使用 Pester 测试此功能?
我在 PowerShell 论坛 here 上读到,我应该能够使用不同的参数筛选器模拟两次相同的命令。所以这就是我的尝试:
function ExecuteInstallRequiredModule ()
{
Install-RequiredModule -ModuleName 'TestModule' -RepositoryName 'TestRepo' `
-ProxyUrl 'http://myproxy'
}
Describe 'Install-RequiredModule' {
$securePassword = "mypassword" |
ConvertTo-SecureString -asPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential ('MyUserName', $securePassword)
Mock Get-Credential { return $psCredential }
# Want to add an error to $Error without it being written to the host.
Mock Install-Module { Write-Error "Some error" -ErrorAction SilentlyContinue } `
-ParameterFilter { $Name -eq 'TestModule' -and $Repository -eq 'TestRepo' -and $ErrorAction -eq 'SilentlyContinue' -and $WarningAction -eq 'SilentlyContinue'}
Mock Install-Module { return $Null } `
-ParameterFilter { $Name -eq 'TestModule' -and $Repository -eq 'TestRepo' -and $Proxy -eq 'http://myproxy' -and $ProxyCredential -eq $psCredential }
Mock Get-InstalledModule { return @('Non-null text') }
It 'attempts to install module a second time if first attempt fails' {
ExecuteInstallRequiredModule
#Assert-VerifiableMock
#Assert-MockCalled Install-Module -Scope It -Times 2
}
}
取消注释被测函数中显示 # throw 'NO error'
的行,我发现在第一次调用 Install-Module 后 $Error.Count 为 0。因此,在第二次调用 Install-Module 之前,不会调用正在创建非终止错误的模拟和函数 returns。
您可以在 try catch 循环中使用 -ErrorAction Stop
调用 install-module
命令。
try
{
#run with no credentials
Install-Module -Name $ModuleName -Repository $RepositoryName -ErrorAction stop -WarningAction SilentlyContinue
}
catch
{
#when fails, run with proxy credentials
Install-Module -Name $ModuleName -Repository $RepositoryName -Proxy $ProxyUrl -ProxyCredential $proxyCredential
}
您可以为同一个 try 命令设置多个 catch{}
块,以捕获不同类型的失败并执行您选择的脚本块。
问题似乎是 Pester 阻止了对公共参数的过滤,因此您对 'ErrorAction' 等的使用导致您的过滤器失败。
您可以在 Pester 模拟代码的第 254 行左右看到从模拟函数中删除的参数:Mock.ps1
此外,测试此删除是 Pester 自己的单元测试之一(第 283 行):Mock.tests.ps1
对于处于类似情况的任何人,这是有效的测试的最终版本:
function Install-RequiredModule (
[string]$ModuleName,
[string]$RepositoryName,
[string]$ProxyUrl
)
{
try
{
Install-Module -Name $ModuleName -Repository $RepositoryName `
-ErrorAction Stop
return
}
catch {}
# There was an error so try again, this time with proxy details.
$proxyCredential = Get-Credential -Message 'Please enter credentials for proxy server'
# No need to Silently Continue this time. We want to see the error details.
$Error.Clear()
Install-Module -Name $ModuleName -Repository $RepositoryName `
-Proxy $ProxyUrl -ProxyCredential $proxyCredential
if ($Error.Count -gt 0)
{
throw $Error[0]
}
if (-not (Get-InstalledModule -Name $ModuleName -ErrorAction SilentlyContinue))
{
throw "Unknown error installing module '$ModuleName' from repository '$RepositoryName'."
}
Write-Output "Module '$ModuleName' successfully installed from repository '$RepositoryName'."
}
#region Tests *************************************************************************************
function ExecuteInstallRequiredModule ()
{
Install-RequiredModule -ModuleName 'TestModule' -RepositoryName 'TestRepo' `
-ProxyUrl 'http://myproxy'
}
Describe 'Install-RequiredModule' {
$securePassword = "mypassword" |
ConvertTo-SecureString -asPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential ('MyUserName', $securePassword)
Mock Get-Credential { return $psCredential }
Mock Install-Module { Write-Error "Some error" }
Mock Install-Module { return $Null } -ParameterFilter { $Proxy -ne $Null -and $ProxyCredential -ne $Null }
Mock Get-InstalledModule { return @('Non-null text') }
It 'attempts to install module a second time, with proxy, if first attempt fails' {
ExecuteInstallRequiredModule
Assert-MockCalled Install-Module -Scope It -Times 2 -Exactly
}
}
#endregion
我有一个要用 Pester 测试的 PowerShell 函数:
function Install-RequiredModule (
[string]$ModuleName,
[string]$RepositoryName,
[string]$ProxyUrl
)
{
# Errors from Install-Module are non-terminating. They won't be caught using
# try - catch. So check $Error instead.
# Clear errors so we know if one shows up it must have been due to Install-Module.
$Error.Clear()
# Want to fail silently, without displaying anything in console to scare the user,
# because it's valid for Install-Module to fail for a user behind a proxy server.
Install-Module -Name $ModuleName -Repository $RepositoryName `
-ErrorAction SilentlyContinue -WarningAction SilentlyContinue
if ($Error.Count -eq 0)
{
# throw 'NO error'
return
}
# There was an error so try again, this time with proxy details.
$proxyCredential = Get-Credential -Message 'Please enter credentials for proxy server'
# No need to Silently Continue this time. We want to see the error details.
$Error.Clear()
Install-Module -Name $ModuleName -Repository $RepositoryName `
-Proxy $ProxyUrl -ProxyCredential $proxyCredential
if ($Error.Count -gt 0)
{
throw $Error[0]
}
if (-not (Get-InstalledModule -Name $ModuleName -ErrorAction SilentlyContinue))
{
throw "Unknown error installing module '$ModuleName' from repository '$RepositoryName'."
}
Write-Output "Module '$ModuleName' successfully installed from repository '$RepositoryName'."
}
这个函数可以调用Install-Module两次。它首先在没有代理凭据的情况下尝试,就好像它可以直接访问互联网一样。如果失败,它会再次尝试,这次使用代理凭据。
如何使用 Pester 测试此功能?
我在 PowerShell 论坛 here 上读到,我应该能够使用不同的参数筛选器模拟两次相同的命令。所以这就是我的尝试:
function ExecuteInstallRequiredModule ()
{
Install-RequiredModule -ModuleName 'TestModule' -RepositoryName 'TestRepo' `
-ProxyUrl 'http://myproxy'
}
Describe 'Install-RequiredModule' {
$securePassword = "mypassword" |
ConvertTo-SecureString -asPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential ('MyUserName', $securePassword)
Mock Get-Credential { return $psCredential }
# Want to add an error to $Error without it being written to the host.
Mock Install-Module { Write-Error "Some error" -ErrorAction SilentlyContinue } `
-ParameterFilter { $Name -eq 'TestModule' -and $Repository -eq 'TestRepo' -and $ErrorAction -eq 'SilentlyContinue' -and $WarningAction -eq 'SilentlyContinue'}
Mock Install-Module { return $Null } `
-ParameterFilter { $Name -eq 'TestModule' -and $Repository -eq 'TestRepo' -and $Proxy -eq 'http://myproxy' -and $ProxyCredential -eq $psCredential }
Mock Get-InstalledModule { return @('Non-null text') }
It 'attempts to install module a second time if first attempt fails' {
ExecuteInstallRequiredModule
#Assert-VerifiableMock
#Assert-MockCalled Install-Module -Scope It -Times 2
}
}
取消注释被测函数中显示 # throw 'NO error'
的行,我发现在第一次调用 Install-Module 后 $Error.Count 为 0。因此,在第二次调用 Install-Module 之前,不会调用正在创建非终止错误的模拟和函数 returns。
您可以在 try catch 循环中使用 -ErrorAction Stop
调用 install-module
命令。
try
{
#run with no credentials
Install-Module -Name $ModuleName -Repository $RepositoryName -ErrorAction stop -WarningAction SilentlyContinue
}
catch
{
#when fails, run with proxy credentials
Install-Module -Name $ModuleName -Repository $RepositoryName -Proxy $ProxyUrl -ProxyCredential $proxyCredential
}
您可以为同一个 try 命令设置多个 catch{}
块,以捕获不同类型的失败并执行您选择的脚本块。
问题似乎是 Pester 阻止了对公共参数的过滤,因此您对 'ErrorAction' 等的使用导致您的过滤器失败。
您可以在 Pester 模拟代码的第 254 行左右看到从模拟函数中删除的参数:Mock.ps1
此外,测试此删除是 Pester 自己的单元测试之一(第 283 行):Mock.tests.ps1
对于处于类似情况的任何人,这是有效的测试的最终版本:
function Install-RequiredModule (
[string]$ModuleName,
[string]$RepositoryName,
[string]$ProxyUrl
)
{
try
{
Install-Module -Name $ModuleName -Repository $RepositoryName `
-ErrorAction Stop
return
}
catch {}
# There was an error so try again, this time with proxy details.
$proxyCredential = Get-Credential -Message 'Please enter credentials for proxy server'
# No need to Silently Continue this time. We want to see the error details.
$Error.Clear()
Install-Module -Name $ModuleName -Repository $RepositoryName `
-Proxy $ProxyUrl -ProxyCredential $proxyCredential
if ($Error.Count -gt 0)
{
throw $Error[0]
}
if (-not (Get-InstalledModule -Name $ModuleName -ErrorAction SilentlyContinue))
{
throw "Unknown error installing module '$ModuleName' from repository '$RepositoryName'."
}
Write-Output "Module '$ModuleName' successfully installed from repository '$RepositoryName'."
}
#region Tests *************************************************************************************
function ExecuteInstallRequiredModule ()
{
Install-RequiredModule -ModuleName 'TestModule' -RepositoryName 'TestRepo' `
-ProxyUrl 'http://myproxy'
}
Describe 'Install-RequiredModule' {
$securePassword = "mypassword" |
ConvertTo-SecureString -asPlainText -Force
$psCredential = New-Object System.Management.Automation.PSCredential ('MyUserName', $securePassword)
Mock Get-Credential { return $psCredential }
Mock Install-Module { Write-Error "Some error" }
Mock Install-Module { return $Null } -ParameterFilter { $Proxy -ne $Null -and $ProxyCredential -ne $Null }
Mock Get-InstalledModule { return @('Non-null text') }
It 'attempts to install module a second time, with proxy, if first attempt fails' {
ExecuteInstallRequiredModule
Assert-MockCalled Install-Module -Scope It -Times 2 -Exactly
}
}
#endregion