纠缠单元测试 - 模拟 Get-ADUser 未按预期工作
Pester Unit Test - Mock Get-ADUser Not Working as Expected
首先,如果这不是提出这些问题的正确方式,我会道歉,这是我第一次提交给 SO。
简短版本是:
我有一个脚本 Get-SamAccountName.ps1
,它接受 -FirstName, -LastName, -Format
的输入变量,然后根据格式生成一个新的 SamAccountName。现在函数本身工作得很好(针对 AD 对象进行测试以验证)。
然而,当我尝试为此编写一些 Pester 测试时,我遇到了一些麻烦,特别是围绕 Pester 的 Mock 函数。
函数Get-SamAccountName
的相关部分如下(去除了参数块等不相关的项目):
function Get-SamAccountName {
param(
[string[]]$FirstName,
[string[]]$LastName,
[string[]]$Format
)
................
# If SamAccountName already in use, add a number until you find a free SamAccountName to use.
if (Get-ADUser -Filter {samaccountname -eq $BaseSam} -ErrorAction SilentlyContinue) {
$index = 0
do {
$index++
$SamAccountName = "{0}{1}" -f $BaseSam.ToLower(),$index
} until (-not(Get-ADUser -Filter {samaccountname -eq $SamAccountName} -ErrorAction SilentlyContinue))
} else {
$SamAccountName = $BaseSam.tolower()
}
return $SamAccountName
}
函数的这一部分是使用 Get-ADUser
的地方,也是我试图在我的 Pester 测试中模拟的内容。本质上是根据名字和姓氏生成的基本 SamAccountName $BaseSam
,然后根据 AD 检查它是否存在,如果它确实存在,则在每次迭代的 SamAccountName 末尾添加一个 #。
我的 Pester 测试文件如下所示,与上面一样去掉了其他有效的测试(以第一个为例):
$ModuleRoot = $env:BHModulePath
$ModuleName = $env:BHProjectName
$ModulePath = $env:BHPSModuleManifest
Remove-Module -Name $ModuleName -ErrorAction 'SilentlyContinue'
Import-Module $ModulePath -Force
InModuleScope $ModuleName {
BeforeAll {
# Load Public function
. "$ModuleRoot\Public\Get-SamAccountName.ps1"
function Get-ADUser ($Filter) {}
}
Describe -Tags ('Unit') "Unit Tests for Get-SamAccountName" {
#Run tests based upon the Examples from the Function Docs
................
Context -Tags ('Unit') "Using the -FLast Format" {
It "Returns <SamAccountName> SamAccountName from FirstName <FirstName> and LastName <LastName> Inputs in the -FLast format." -TestCases @(
#Test with string containing Diacritic characters.
@{ FirstName = "Clärk"; LastName = "Ként"; SamAccountName = "ckent"}
#Test with string containing Latin characters only.
@{ FirstName = "Bruce"; LastName = "Wayne"; SamAccountName = "bwayne"}
) {
Get-SamAccountName -FirstName $FirstName -LastName $LastName -Format 'FLast' | Should -Be $SamAccountName
}
}
................
Context -Tags ('Unit') "A User Already Exists in Active Directory" {
It "Returns <expected> SamAccountName from FirstName <FirstName> and LastName <LastName> with an increment number." -TestCases @(
#Test with string containing Diacritic characters.
@{ FirstName = "Clärk"; LastName = "Ként"; BaseSam = "ckent"; expected = "ckent1"}
#Test with string containing Latin characters only.
@{ FirstName = "Bruce"; LastName = "Wayne"; BaseSam = "bwayne"; expected = "bwayne"}
) {
Mock Get-ADUser -MockWith { $BaseSam } -ParameterFilter { $Filter -eq {samaccountname -eq $BaseSam} }
Get-SamAccountName -FirstName $FirstName -LastName $LastName -Format 'FLast' | Should -Be $expected
}
}
}
}
现在,我现在故意有不同的预期,因为我想看到一个测试,其中一个失败,一个通过以验证它是否已正确设置,最终结果都设置为 username1
(附加的 1 用于验证已发现现有 AD 用户)。
运行 这在技术上与上面一样工作,但它与我想要实现的相反(下面的纠缠输出):
Describing Unit Tests for Get-SamAccountName
Context A User Already Exists in Active Directory
[-] Returns ckent1 SamAccountName from FirstName Clärk and LastName Ként with an increment number. 12ms (10ms|2ms)
Expected strings to be the same, but they were different.
Expected length: 9
Actual length: 8
Strings differ at index 8.
Expected: 'ckent1'
But was: 'ckent'
--------^
at Get-SamAccountName -FirstName $FirstName -LastName $LastName -Format 'FLast' | Should -Be $expected, /mnt/k/Repositories/CodeBlue.UserAutomation/Tests/Unit/Get-SamAccountName.Unit.Tests.ps1:70
at <ScriptBlock>, /mnt/k/Repositories/CodeBlue.UserAutomation/Tests/Unit/Get-SamAccountName.Unit.Tests.ps1:70
[+] Returns bwayne SamAccountName from FirstName Bruce and LastName Wayne with an increment number. 9ms (8ms|1ms)
Tests completed in 281ms
Tests Passed: 1, Failed: 1, Skipped: 0 NotRun: 8
这 100% 可能是我的语法或项目的位置问题,我现在只使用 Pester 大约一个星期,从技术上讲,这是我第一次尝试使用 Mock。在这里阅读了无数关于 SO 的主题以及带有示例的博客条目,但我终其一生都无法让这个特定的用例按预期工作:(
我会注意到以上只是我尝试过的 Mock 的一次迭代,但是我设法获得的唯一一个 'working' 尽管与我尝试做的方式相反.
如有任何帮助,我们将不胜感激 <3
这很棘手。在我看来有两个问题:
- 您的
Mock
没有触发,因为 -ParameterFilter
与 Get-ADUser
的调用不匹配。
- 你需要你的测试以两种不同的方式模拟
Get-ADUser
,这样当第一次使用它时 returns 一个结果但是当第二次调用时它 returns 什么都没有。
我已经创建了我认为可行的解决方案,但是因为让 -ParameterFilter
与 Get-ADUser
的 -Filter
参数一起工作似乎非常困难,所以我更改了您的函数,以便其中一个 Get-ADUser
调用包含 -Properties
参数,这样我就可以根据它来区分用途。另一种方法可能是在 Get-ADUser
周围创建两个包装函数,以便在第一次和第二次使用它时使用。
这是我的解决方案。我还必须编辑您的函数,因为我缺少您设置 $BaseSam
的部分。这适用于 Bruce Wayne 但不适用于 Clark Kent,因为它不处理外国字符。
BeforeAll {
function Get-SamAccountName {
param(
[string]$FirstName,
[string[]]$LastName,
[string[]]$Format
)
$BaseSam = $FirstName[0] + $LastName
# If SamAccountName already in use, add a number until you find a free SamAccountName to use.
if (Get-ADUser -Filter { samaccountname -eq $BaseSam } -Properties SamAccountName -ErrorAction SilentlyContinue) {
$index = 0
do {
$index++
$SamAccountName = "{0}{1}" -f $BaseSam.ToLower(), $index
} until (-not(Get-ADUser -Filter { samaccountname -eq $SamAccountName } -ErrorAction SilentlyContinue))
}
else {
$SamAccountName = $BaseSam.tolower()
}
return $SamAccountName
}
function Get-ADUser {
param( $Filter, $Properties)
}
}
Describe -Tags ('Unit') "Unit Tests for Get-SamAccountName" {
Context -Tags ('Unit') "A User Already Exists in Active Directory" {
It "Returns <expected> SamAccountName from FirstName <FirstName> and LastName <LastName> with an increment number." -TestCases @(
#Test with string containing Diacritic characters.
@{ FirstName = "Clärk"; LastName = "Ként"; BaseSam = "ckent"; expected = "ckent1" }
#Test with string containing Latin characters only.
@{ FirstName = "Bruce"; LastName = "Wayne"; BaseSam = "bwayne"; expected = "bwayne1" }
) {
Mock Get-ADUser -MockWith { $BaseSam } -ParameterFilter { $Properties -eq 'SamAccountName' }
Mock Get-ADUser -MockWith { }
Get-SamAccountName -FirstName $FirstName -LastName $LastName -BaseSam $BaseSam -Format 'FLast' | Should -Be $expected
}
}
}
请注意,我的示例在 BeforeAll
中包含您的功能,只是为了便于测试,您可以继续导入您的模块。
首先,如果这不是提出这些问题的正确方式,我会道歉,这是我第一次提交给 SO。
简短版本是:
我有一个脚本 Get-SamAccountName.ps1
,它接受 -FirstName, -LastName, -Format
的输入变量,然后根据格式生成一个新的 SamAccountName。现在函数本身工作得很好(针对 AD 对象进行测试以验证)。
然而,当我尝试为此编写一些 Pester 测试时,我遇到了一些麻烦,特别是围绕 Pester 的 Mock 函数。
函数Get-SamAccountName
的相关部分如下(去除了参数块等不相关的项目):
function Get-SamAccountName {
param(
[string[]]$FirstName,
[string[]]$LastName,
[string[]]$Format
)
................
# If SamAccountName already in use, add a number until you find a free SamAccountName to use.
if (Get-ADUser -Filter {samaccountname -eq $BaseSam} -ErrorAction SilentlyContinue) {
$index = 0
do {
$index++
$SamAccountName = "{0}{1}" -f $BaseSam.ToLower(),$index
} until (-not(Get-ADUser -Filter {samaccountname -eq $SamAccountName} -ErrorAction SilentlyContinue))
} else {
$SamAccountName = $BaseSam.tolower()
}
return $SamAccountName
}
函数的这一部分是使用 Get-ADUser
的地方,也是我试图在我的 Pester 测试中模拟的内容。本质上是根据名字和姓氏生成的基本 SamAccountName $BaseSam
,然后根据 AD 检查它是否存在,如果它确实存在,则在每次迭代的 SamAccountName 末尾添加一个 #。
我的 Pester 测试文件如下所示,与上面一样去掉了其他有效的测试(以第一个为例):
$ModuleRoot = $env:BHModulePath
$ModuleName = $env:BHProjectName
$ModulePath = $env:BHPSModuleManifest
Remove-Module -Name $ModuleName -ErrorAction 'SilentlyContinue'
Import-Module $ModulePath -Force
InModuleScope $ModuleName {
BeforeAll {
# Load Public function
. "$ModuleRoot\Public\Get-SamAccountName.ps1"
function Get-ADUser ($Filter) {}
}
Describe -Tags ('Unit') "Unit Tests for Get-SamAccountName" {
#Run tests based upon the Examples from the Function Docs
................
Context -Tags ('Unit') "Using the -FLast Format" {
It "Returns <SamAccountName> SamAccountName from FirstName <FirstName> and LastName <LastName> Inputs in the -FLast format." -TestCases @(
#Test with string containing Diacritic characters.
@{ FirstName = "Clärk"; LastName = "Ként"; SamAccountName = "ckent"}
#Test with string containing Latin characters only.
@{ FirstName = "Bruce"; LastName = "Wayne"; SamAccountName = "bwayne"}
) {
Get-SamAccountName -FirstName $FirstName -LastName $LastName -Format 'FLast' | Should -Be $SamAccountName
}
}
................
Context -Tags ('Unit') "A User Already Exists in Active Directory" {
It "Returns <expected> SamAccountName from FirstName <FirstName> and LastName <LastName> with an increment number." -TestCases @(
#Test with string containing Diacritic characters.
@{ FirstName = "Clärk"; LastName = "Ként"; BaseSam = "ckent"; expected = "ckent1"}
#Test with string containing Latin characters only.
@{ FirstName = "Bruce"; LastName = "Wayne"; BaseSam = "bwayne"; expected = "bwayne"}
) {
Mock Get-ADUser -MockWith { $BaseSam } -ParameterFilter { $Filter -eq {samaccountname -eq $BaseSam} }
Get-SamAccountName -FirstName $FirstName -LastName $LastName -Format 'FLast' | Should -Be $expected
}
}
}
}
现在,我现在故意有不同的预期,因为我想看到一个测试,其中一个失败,一个通过以验证它是否已正确设置,最终结果都设置为 username1
(附加的 1 用于验证已发现现有 AD 用户)。
运行 这在技术上与上面一样工作,但它与我想要实现的相反(下面的纠缠输出):
Describing Unit Tests for Get-SamAccountName
Context A User Already Exists in Active Directory
[-] Returns ckent1 SamAccountName from FirstName Clärk and LastName Ként with an increment number. 12ms (10ms|2ms)
Expected strings to be the same, but they were different.
Expected length: 9
Actual length: 8
Strings differ at index 8.
Expected: 'ckent1'
But was: 'ckent'
--------^
at Get-SamAccountName -FirstName $FirstName -LastName $LastName -Format 'FLast' | Should -Be $expected, /mnt/k/Repositories/CodeBlue.UserAutomation/Tests/Unit/Get-SamAccountName.Unit.Tests.ps1:70
at <ScriptBlock>, /mnt/k/Repositories/CodeBlue.UserAutomation/Tests/Unit/Get-SamAccountName.Unit.Tests.ps1:70
[+] Returns bwayne SamAccountName from FirstName Bruce and LastName Wayne with an increment number. 9ms (8ms|1ms)
Tests completed in 281ms
Tests Passed: 1, Failed: 1, Skipped: 0 NotRun: 8
这 100% 可能是我的语法或项目的位置问题,我现在只使用 Pester 大约一个星期,从技术上讲,这是我第一次尝试使用 Mock。在这里阅读了无数关于 SO 的主题以及带有示例的博客条目,但我终其一生都无法让这个特定的用例按预期工作:(
我会注意到以上只是我尝试过的 Mock 的一次迭代,但是我设法获得的唯一一个 'working' 尽管与我尝试做的方式相反.
如有任何帮助,我们将不胜感激 <3
这很棘手。在我看来有两个问题:
- 您的
Mock
没有触发,因为-ParameterFilter
与Get-ADUser
的调用不匹配。 - 你需要你的测试以两种不同的方式模拟
Get-ADUser
,这样当第一次使用它时 returns 一个结果但是当第二次调用时它 returns 什么都没有。
我已经创建了我认为可行的解决方案,但是因为让 -ParameterFilter
与 Get-ADUser
的 -Filter
参数一起工作似乎非常困难,所以我更改了您的函数,以便其中一个 Get-ADUser
调用包含 -Properties
参数,这样我就可以根据它来区分用途。另一种方法可能是在 Get-ADUser
周围创建两个包装函数,以便在第一次和第二次使用它时使用。
这是我的解决方案。我还必须编辑您的函数,因为我缺少您设置 $BaseSam
的部分。这适用于 Bruce Wayne 但不适用于 Clark Kent,因为它不处理外国字符。
BeforeAll {
function Get-SamAccountName {
param(
[string]$FirstName,
[string[]]$LastName,
[string[]]$Format
)
$BaseSam = $FirstName[0] + $LastName
# If SamAccountName already in use, add a number until you find a free SamAccountName to use.
if (Get-ADUser -Filter { samaccountname -eq $BaseSam } -Properties SamAccountName -ErrorAction SilentlyContinue) {
$index = 0
do {
$index++
$SamAccountName = "{0}{1}" -f $BaseSam.ToLower(), $index
} until (-not(Get-ADUser -Filter { samaccountname -eq $SamAccountName } -ErrorAction SilentlyContinue))
}
else {
$SamAccountName = $BaseSam.tolower()
}
return $SamAccountName
}
function Get-ADUser {
param( $Filter, $Properties)
}
}
Describe -Tags ('Unit') "Unit Tests for Get-SamAccountName" {
Context -Tags ('Unit') "A User Already Exists in Active Directory" {
It "Returns <expected> SamAccountName from FirstName <FirstName> and LastName <LastName> with an increment number." -TestCases @(
#Test with string containing Diacritic characters.
@{ FirstName = "Clärk"; LastName = "Ként"; BaseSam = "ckent"; expected = "ckent1" }
#Test with string containing Latin characters only.
@{ FirstName = "Bruce"; LastName = "Wayne"; BaseSam = "bwayne"; expected = "bwayne1" }
) {
Mock Get-ADUser -MockWith { $BaseSam } -ParameterFilter { $Properties -eq 'SamAccountName' }
Mock Get-ADUser -MockWith { }
Get-SamAccountName -FirstName $FirstName -LastName $LastName -BaseSam $BaseSam -Format 'FLast' | Should -Be $expected
}
}
}
请注意,我的示例在 BeforeAll
中包含您的功能,只是为了便于测试,您可以继续导入您的模块。