Reputation: 376
I am converting Pester tests from V4 to V5 and in line with the best practices, moving the loops from foreach()
to Describe -ForEach{}
. The tests are a standard set that checks if each function has comment based help and this requires multiple nested loops.
This question is similar to Pester 5 variable scoping and Working with nested foreach and I have referred to Data Driven Tests, but my problem goes deeper as firstly, I need multiple levels of nested loops and secondly, I need access to multiple variables. The first variable $helpParameterNames
is used by the loop and the value of each row is checked to see if it exists in the second variable $parameterNames
V4
In V4, the arrays are accessible throughout the whole set of Describe/Context/It blocks so when I get to the last foreach
loop, both $helpParameterNames
and $parameterNames
are accessible.
$commands = Get-Command -Module ModuleName
foreach ($command in $commands) {
$commandName = $command.Name
$help = Get-Help $commandName -ErrorAction SilentlyContinue
Describe "Test help for $commandName" -Tag Help {
<It Tests Removed>
Context "Test parameter help for $commandName" {
$common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable'
$parameters = $command.ParameterSets.Parameters | Sort-Object -Property Name -Unique | Where-Object Name -notin $common
$parameterNames = $parameters.Name
$helpParameterNames = $help.Parameters.Parameter.Name | Sort-Object -Unique
foreach ($parameter in $parameters) {
$parameterName = $parameter.Name
$parameterHelp = $help.parameters.parameter | Where-Object Name -EQ $parameterName
<It Tests Removed>
}
foreach ($helpParm in $helpParameterNames) {
It "Should find parameter $helpParm in the function" {
$helpParm -in $parameterNames | Should Be $true
}
}
}
}
V5
In V5 as the setup code has to be defined in the BeforeDiscovery
block, I can't get it to be available in the sub-blocks automatically. I can make it accessible by passing the array into the -ForEach
as a HashTable, but then it will treat it as 1 object and not iterate over it, which is fine for $help
, but not for $helpParameterNames
BeforeDiscovery {
$commands = Get-Command -Module ModuleName
}
Describe "Help Rules" -Tags Build, Help -ForEach $commands {
BeforeDiscovery {
$commandName = $_.Name
$common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable'
$parameters = (Get-Command $commandName).ParameterSets.Parameters | Where-Object -Property Name -NotIn $common | Sort-Object -Property Name -Unique
$parameterNames = $parameters.Name
$help = Get-Help $commandName -Full -ErrorAction SilentlyContinue
}
Context "General Rules for $commandName" -ForEach @{help = $help} {
<It Tests Removed>
Describe "Parameter Rules for <parameter.Name>" -Tags Help, Parameter -ForEach $parameters {
BeforeAll {
$parameter = $_.parameters
$helpParameterNames = $help.Parameters.Parameter.Name | Sort-Object -Unique
$parameterHelp = $help.parameters.parameter | Where-Object Name -EQ $parameter.Name
}
<It Tests Removed>
}
It "Should find parameter <helpParameter> in the function" -Tag Help, Parameter -ForEach $helpParameterNames {
BeforeAll {
$helpParameter = $_
}
$helpParameter -in $parameterNames | Should -BeTrue
}
}
How do I get the last -ForEach
to have access to both $parameterNames
from the BeforeDiscovery
block and also $helpParameterNames
from the parent loop?
Upvotes: 0
Views: 414
Reputation: 376
This is a unique situation and the solution is that some code has to be repeated both in BeforeDiscovery
and BeforeAll
. Adding the code to BeforeDiscovery
means that it can be used in the Context -ForEach
and adding the code to BeforeAll
means that it is accessible as a single object inside the child Describe
blocks.
Describe "Help Rules" -Tags Build, Help -ForEach $commands {
BeforeDiscovery {
$common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable'
$parameters = (Get-Command $_.Name).ParameterSets.Parameters | Where-Object -Property Name -NotIn $common | Sort-Object -Property Name -Unique
$help = Get-Help $_.Name -Full -ErrorAction SilentlyContinue
$helpParameterNames = $help.Parameters.Parameter.Name | Sort-Object -Unique
}
BeforeAll {
$commandName = $_.Name
$help = Get-Help $commandName -Full -ErrorAction SilentlyContinue
$helpParameterNames = $help.Parameters.Parameter.Name | Sort-Object -Unique
#Define this both in BeforeDiscovery and BeforeAll as it is needed both in the ForEach and also as an array
$common = 'Debug', 'ErrorAction', 'ErrorVariable', 'InformationAction', 'InformationVariable', 'OutBuffer', 'OutVariable', 'PipelineVariable', 'Verbose', 'WarningAction', 'WarningVariable'
$parameters = (Get-Command $_.Name).ParameterSets.Parameters | Where-Object -Property Name -NotIn $common | Sort-Object -Property Name -Unique
$parameterNames = $parameters.Name
}
Context "General Rules for <commandName>" -ForEach $parameters {
Upvotes: 0