Pester:并非所有模拟函数都被拦截
Pester: not all mocked functions are intercepted
我有很多模块,包括 ModuleMain 和 ModuleSql。模块之间存在相互依赖性,因此 ModuleMain 中的 Main-Function 使用 ModuleSql:
中的 4 个函数
function Main-Function {
[CmdletBinding(SupportsShouldProcess=$true)]
# The following 4 lines are all wrapped in $PSCmdlet.ShouldProcess() and
# try {} catch {} logic. I have left that out in this post, but I mention
# it in case it is relevant.
$a = New-SqlConnection
$b = Invoke-SqlStoredProc -connection $a
$c = Invoke-SqlQuery -connection $a
Close-SqlConnection -connection $a | Out-Null
return $c
}
我创建了一个 Function-Main1.tests.ps1 文件来测试 Function-Main1。起初我使用 InModuleScope
但后来切换到使用 -ModuleName
参数指定每个模拟的模块。
Import-Module "ModuleMain" -Force
Describe "Main-Function" {
Mock -ModuleName ModuleMain New-SqlConnection {
return $true }
Mock -ModuleName ModuleMain Invoke-SqlStoredProc {
return $true }
Mock -ModuleName ModuleMain Invoke-SqlQuery {
return $true }
Mock -ModuleName ModuleMain Close-SqlConnection {
return $true }
Context "When calling Main-Function with mocked SQL functions" {
It "Calls each SQL function once" {
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName New-SqlConnecion -Times 1 -Exactly
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlStoredProc -Times 1 -Exactly
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlQuery -Times 1 -Exactly
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Close-SqlConnecion -Times 1 -Exactly
}
}
}
当我 运行 这个测试时,我得到以下结果:
[-] Calls each SQL function once 223ms
Expected Invoke-SqlStoredProc in module ModuleMain to be called 1 times exactly but was called 0 times
at line: xx in
xx: Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlStoredProc -Times 1 -Exactly
注意以下几点:
- 我没有导入 ModuleSql,其中定义了 -Sql 函数,因为无论如何我都在模拟它们。
- 我 observerd/worked 我需要将
-ModuleName
设置为定义 Main-Function 的模块,而不是SQL 函数(我正在尝试模拟)已定义。
- 我玩过
InModuleScope
和 -ModuleName
,例如将一个或另一个设置为 ModuleSQL,但主要是这只会让事情变得更糟。
通过尝试,在其他模拟函数中添加详细输出,我已经确认 New-SqlConnection
和 Close-SqlConnection
都被拦截了,但是 Invoke-SqlStoredProc
和 Invoke-SqlQuery
不是。
深入探索,我可以看到 Invoke-Sql*
(模拟)函数抛出以下异常:错误:"Invalid cast from 'System.Boolean' to 'System.Data.SqlClient.SqlConnection'." This 是调用这些函数的真实版本时我期望的行为,但我期望模拟版本会忽略参数类型。
为什么 Pester 只会拦截我的 4 个函数中的 2 个?
所以,上面的评论中给出了这个问题的简短答案:
Mocked functions does not ignore parameter types. --PetSerAl
这意味着当我尝试调用 Invoke-Sql*
函数并使用伪造的 $sqlConnection 变量(只需设置为 $true ) 这导致了错误,因为输入参数不是预期的数据类型。
我的解决方案是模拟 New-SqlConnection
函数,因此它返回一个 [System.Data.SqlCient.SqlConnection]
对象。碰巧的是,我也恢复使用 InModuleScope
而不是在每个 Mock 上指定模块:
InModuleScope "ModuleMain" {
Describe "MainFunction" {
Mock New-SqlConnection {
New-Object -TypeName System.Data.SqlCient.SqlConnection
}
...
}
我有很多模块,包括 ModuleMain 和 ModuleSql。模块之间存在相互依赖性,因此 ModuleMain 中的 Main-Function 使用 ModuleSql:
中的 4 个函数function Main-Function {
[CmdletBinding(SupportsShouldProcess=$true)]
# The following 4 lines are all wrapped in $PSCmdlet.ShouldProcess() and
# try {} catch {} logic. I have left that out in this post, but I mention
# it in case it is relevant.
$a = New-SqlConnection
$b = Invoke-SqlStoredProc -connection $a
$c = Invoke-SqlQuery -connection $a
Close-SqlConnection -connection $a | Out-Null
return $c
}
我创建了一个 Function-Main1.tests.ps1 文件来测试 Function-Main1。起初我使用 InModuleScope
但后来切换到使用 -ModuleName
参数指定每个模拟的模块。
Import-Module "ModuleMain" -Force
Describe "Main-Function" {
Mock -ModuleName ModuleMain New-SqlConnection {
return $true }
Mock -ModuleName ModuleMain Invoke-SqlStoredProc {
return $true }
Mock -ModuleName ModuleMain Invoke-SqlQuery {
return $true }
Mock -ModuleName ModuleMain Close-SqlConnection {
return $true }
Context "When calling Main-Function with mocked SQL functions" {
It "Calls each SQL function once" {
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName New-SqlConnecion -Times 1 -Exactly
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlStoredProc -Times 1 -Exactly
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlQuery -Times 1 -Exactly
Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Close-SqlConnecion -Times 1 -Exactly
}
}
}
当我 运行 这个测试时,我得到以下结果:
[-] Calls each SQL function once 223ms
Expected Invoke-SqlStoredProc in module ModuleMain to be called 1 times exactly but was called 0 times
at line: xx in
xx: Assert-MockCalled -Scope Context -ModuleName ModuleMain -CommandName Invoke-SqlStoredProc -Times 1 -Exactly
注意以下几点:
- 我没有导入 ModuleSql,其中定义了 -Sql 函数,因为无论如何我都在模拟它们。
- 我 observerd/worked 我需要将
-ModuleName
设置为定义 Main-Function 的模块,而不是SQL 函数(我正在尝试模拟)已定义。 - 我玩过
InModuleScope
和-ModuleName
,例如将一个或另一个设置为 ModuleSQL,但主要是这只会让事情变得更糟。
通过尝试,在其他模拟函数中添加详细输出,我已经确认 New-SqlConnection
和 Close-SqlConnection
都被拦截了,但是 Invoke-SqlStoredProc
和 Invoke-SqlQuery
不是。
深入探索,我可以看到 Invoke-Sql*
(模拟)函数抛出以下异常:错误:"Invalid cast from 'System.Boolean' to 'System.Data.SqlClient.SqlConnection'." This 是调用这些函数的真实版本时我期望的行为,但我期望模拟版本会忽略参数类型。
为什么 Pester 只会拦截我的 4 个函数中的 2 个?
所以,上面的评论中给出了这个问题的简短答案:
Mocked functions does not ignore parameter types. --PetSerAl
这意味着当我尝试调用 Invoke-Sql*
函数并使用伪造的 $sqlConnection 变量(只需设置为 $true ) 这导致了错误,因为输入参数不是预期的数据类型。
我的解决方案是模拟 New-SqlConnection
函数,因此它返回一个 [System.Data.SqlCient.SqlConnection]
对象。碰巧的是,我也恢复使用 InModuleScope
而不是在每个 Mock 上指定模块:
InModuleScope "ModuleMain" {
Describe "MainFunction" {
Mock New-SqlConnection {
New-Object -TypeName System.Data.SqlCient.SqlConnection
}
...
}