Home > Enterprise >  Use a PowerShell hash's key/values with Pester TestCases
Use a PowerShell hash's key/values with Pester TestCases

Time:10-28

I would like to pass the key/values of a PowerShell hash to a Pester unit test, via the TestCases parameter:

BeforeAll {
  $Expected = @{
    Address1='Address1'
    Address2='Address2'
    City='City'
    RegionCode='RegionCode'
    PostalCode='PostalCode'
  }
}

BeforeEach {
  Mock Invoke-SqlCmd
  Invoke-MyFunction @Expected
}

It "sets the column '<Name>' with the value '<Value>'" -TestCases ( $Optional.GetEnumerator() | ForEach-Object { @{Name=$_.Key; Value=$_.Value} } ) {
    param($Name, $Value)
    
    $Test = "*{0}='{1}'*" -f $Name, $Value

    Assert-MockCalled Invoke-Sqlcmd -ParameterFilter {
        $Query -like $Test
    }

}

But can't seem to get the hash's properties 'shaped' correctly to get the tests to work correctly.

CodePudding user response:

Anything that you need to build the test cases needs to be in a BeforeDiscovery { ... } block. The code in BeforeAll { ... } gets deferred until execution of the tests, however your $expected hashtable needs to be available earlier than that before discovery in order to build each of the tests from your test cases. In addition to that you need to nest your It { ... } blocks in either a Describe or Context block

Update: per your comment - In order to make $Expected available to the test scopes without duplicating the assignment in the BeforeEach block you can set the variable's scope to script scope

BeforeDiscovery {
    $script:Expected = @{
        Address1   = 'Value_Address1'
        Address2   = 'Value_Address2'
        City       = 'Value_City'
        RegionCode = 'Value_RegionCode'
        PostalCode = 'Value_PostalCode'
    }
}

Describe "Need a describe or context block" {
    BeforeEach {
        Mock Invoke-SqlCmd
        Invoke-MyFunction @Expected
    }

    It "sets the column '<Name>' with the value '<Value>'" -TestCases (
         $Expected.GetEnumerator() | 
            ForEach-Object { @{Name = $_.Key; Value = $_.Value } } 
    ) {
        #   param($Name, $Value)

        $Test = "*{0}='{1}'*" -f $Name, $Value

        #   Assert-MockCalled Invoke-Sqlcmd -ParameterFilter {
        #       $Query -like $Test
        #   }

    }
}

Output

Pester v5.3.1

Starting discovery in 1 files.
Discovery found 5 tests in 25ms.
Running tests.

Running tests from 'C:\temp\powershell\pester.tests.ps1'
Describing Need a describe or context block
  [ ] sets the column 'RegionCode' with the value 'Value_RegionCode' 8ms (4ms|4ms)
  [ ] sets the column 'City' with the value 'Value_City' 2ms (1ms|1ms)
  [ ] sets the column 'Address2' with the value 'Value_Address2' 5ms (1ms|3ms)
  [ ] sets the column 'PostalCode' with the value 'Value_PostalCode' 3ms (1ms|2ms)
  [ ] sets the column 'Address1' with the value 'Value_Address1' 3ms (1ms|2ms)
Tests completed in 168ms
Tests Passed: 5, Failed: 0, Skipped: 0 NotRun: 0

See Pester's v5 Discovery and Run docs

Paraphrased from that page:
This is what happens during the initial Discovery phase when you run Invoke-Pester

  • Test script file is invoked
  • BeforeAll function runs ONLY saving the scriptblock provided to it, not executing it (yet) Hashtable and other variables inside this block have not been created yet
  • Describe function runs AND invokes the scriptblock provided to it in order to collect information about the tests contained inside it. Note: Describe and Context scriptblocks are the only scriptblocks that are run during discovery
  • All parameters (including -TestCases) that are provided to the It function are evaluated by PowerShell (this is where the hashtable is needed however sadly it hasn't been brought into existence yet)
  • It function runs saving (NOT running) the code contained generating the tests, 1 per each item in the previously evaluated -TestCases array
  • Write-Host "Discovery done." is run.
  • Related