Open taddison opened 3 years ago
Here is an example of creating dynamic It
blocks based on a file-level set of databases, in addition to a per-test exclusion set. In cases where you don't need to perform additional checks you can use the file-level variable directly.
BeforeDiscovery {
$databases = @("One", "Two", "Skip")
}
Describe "Iterate the array of databases" {
BeforeDiscovery {
$databasesToCheck = $databases | Where-Object { $_ -ne "Skip" }
}
It "<_> should exist" -ForEach $databasesToCheck {
$database = $_
$database | Should -Not -Be "SKip"
}
}
And here is an example of injecting external context (e.g. $config
).
param (
[Parameter(Mandatory)]
[string] $DatabaseToSkip
)
BeforeDiscovery {
$databases = @("One", "Two", "Skip")
}
Describe "Iterate the array of databases" {
BeforeDiscovery {
$databasesToCheck = $databases | Where-Object { $_ -ne $DatabaseToSkip }
}
It "<_> should exist" -ForEach $databasesToCheck {
$database = $_
$database | Should -Not -Be "Skip"
}
}
Invoked via:
$container = New-PesterContainer -Path "*.*" -Data @{ DatabaseToSkip = "Skip" }
Invoke-Pester -Container $container -Output Detailed
To allow a describe block to only execute if the config option is present, we can use the -ForEach
argument there too. I've created a wrapper function SqlChecksDescribe
which allows the test to be written more tersely.
I believe this will allow invocation of a whole config file, and rather than needing to loop through each config value and Invoke-Pester -Tag ..
that can all be pushed into the test itself.
I'd like to skip passing $checks if possible, and maybe even come up with a convention based way to extract the tag from the test - if each describe had to contain the Tag to start, e.g. TagName - TagDescription
, then the Tag could be extracted from the description and the invocation would be even terser:
SqlChecksDescribe "SSBEnabled - Service Broker Enabled" $checks { ... }
Maybe a factory function that accepts $checks
and could then be invoked once per-file? Not sure on exactly how discovery works and if that would be possible (and rules around conditional function definitions in powershell).
param (
[Parameter(Mandatory)]
[string] $DatabaseToSkip
)
BeforeDiscovery {
$databases = @("One", "Two", "Skip")
$checks = @("A", "B")
}
Function SqlChecksDescribe {
[CmdletBinding()]
Param(
[Parameter(Mandatory, Position = 0)]
$Name,
[Parameter(Mandatory, Position = 1)]
$Tag,
[Parameter(Mandatory, Position = 2)]
$Checks,
[Parameter(Mandatory, Position = 3)]
$Fixture
)
Describe -Name $name -Tag $Tag -ForEach @($Checks | Where-Object { $_ -eq $Tag }) -Fixture $Fixture
}
Describe "Iterate the array of databases" {
BeforeDiscovery {
$databasesToCheck = $databases | Where-Object { $_ -ne $DatabaseToSkip }
}
It "<_> should exist" -ForEach $databasesToCheck {
$database = $_
$database | Should -Not -Be "Skip"
}
}
Describe "A description" -Tag "A" -ForEach @($checks | Where-Object { $_ -eq "A" }) {
It "should run" {
$true | Should -Be $true
}
}
SqlChecksDescribe "B Description" "B" $checks {
It "should run" {
$true | Should -Be $true
}
}
SqlChecksDescribe "C Description" "C" $checks {
It "should not run" {
$true | Should -Be $false
}
}
Turns out it's fairly trivial to do what I've suggested above - each test file needs to contain a wrapper function SqlChecksDescribe
that captures the value of $checks and passes everything on to the shared function SqlChecksDescribeShared
.
param (
[Parameter(Mandatory)]
[string] $DatabaseToSkip
)
BeforeDiscovery {
$databases = @("One", "Two", "Skip")
$checks = @("A", "B")
Function SqlChecksDescribe {
[CmdletBinding()]
Param(
[Parameter(Mandatory, Position = 0)]
$Name,
[Parameter(Mandatory, Position = 1)]
$Fixture
)
SqlChecksDescribeShared -Name $name -Checks $checks -Fixture $Fixture
}
}
Function SqlChecksDescribeShared {
[CmdletBinding()]
Param(
[Parameter(Mandatory, Position = 0)]
$Name,
[Parameter(Mandatory, Position = 1)]
$Checks,
[Parameter(Mandatory, Position = 2)]
$Fixture
)
$firstSpace = $Name.IndexOf(" ")
if ($firstSpace -eq -1) {
$Tag = $Name
}
else {
$Tag = $Name.Substring(0, $firstSpace)
}
Describe -Name $name -Tag $Tag -ForEach @($Checks | Where-Object { $_ -eq $Tag }) -Fixture $Fixture
}
Describe "Iterate the array of databases" {
BeforeDiscovery {
$databasesToCheck = $databases | Where-Object { $_ -ne $DatabaseToSkip }
}
It "<_> should exist" -ForEach $databasesToCheck {
$database = $_
$database | Should -Not -Be "Skip"
}
}
Describe "A description" -Tag "A" -ForEach @($checks | Where-Object { $_ -eq "A" }) {
It "should run" {
$true | Should -Be $true
}
}
SqlChecksDescribe "B Description" {
It "should run" {
$true | Should -Be $true
}
}
SqlChecksDescribe "C Description" {
It "should not run" {
$true | Should -Be $false
}
}
With enhancements to v5 (see https://pester-docs.netlify.app/docs/usage/data-driven-tests) it should be possible to migrate.
Roughly, this would entail:
There may also be some advantages to this approach that can be realized by having the list of e.g. $databasesToCheck persist without requiring a cache (as discovery runs once).