3

I recently switched over to Pester 5.0.2 to start doing some testing on a Powershell script I wrote. I am able to break into the BeforeAll block and the paths are being populated as I would expect, however when I get to the Get-ChildItem area in my Describe block the variables from BeforeAll are $null.

BeforeAll {
    $testDir = Split-Path $PSCommandPath -Parent
    $prodDir = Split-Path $testDir -Parent
}
Describe "Tests - All Files in prod" {
    Get-ChildItem $prodDir -File | ForEach-Object {
        $fileName = (Split-Path $_ -leaf).PadRight(20, ' ')
        $filePath = $_.PSPath
        It "Vars not declared or changed between function definitions" {
            [System.Collections.ArrayList]$preSource = (Get-Variable).Name
            . "$filePath" -Sourcing
            [System.Collections.ArrayList]$postSource = (Get-Variable).Name
            $postSource.Remove('preSource')
            $postSource.Remove('UnderTest')
            [array]$diff = Compare-Object $preSource $postSource -PassThru
            $diff | Should -BeNullOrEmpty
        }
        It "$fileName all #*, #?, #TODO tags are removed" {
            $_ | Should -Not -FileContentMatch $([regex]::escape('#TODO'))
            $_ | Should -Not -FileContentMatch $([regex]::escape('#*'))
            $_ | Should -Not -FileContentMatch $([regex]::escape('#?'))
        }
    }
}
Lieven Keersmaekers
  • 57,207
  • 13
  • 112
  • 146
Efie
  • 1,430
  • 2
  • 14
  • 34

1 Answers1

3

I found the answer, but it was a little hard to find in the documentation. This doesn't work any longer because of the new 'Discovery' feature. This runs before all of the Describe,It,Context,BeforeAll,AfterAll, etc... blocks. The code that was run here is then discarded.

I ended up using test cases using the -TestCase parameter to resolve the issue.

EDIT:

Below is version with comments on why it won't work and after that an example on how to set it up.

BeforeAll {
    # This block will run AFTER discovery. Anything you need to be evaluated in front of the tests should
    # be placed here
    $testDir = Split-Path $PSCommandPath -Parent
    $prodDir = Split-Path $testDir -Parent
}
$iWillDisappearAfterDiscovery = 'Byebye'
Describe "Describing your tests" {
    # Code placed here will be run DURING discovery, and will not be availble at runtime. Placing the code from
    # BeforeAll here instead would not work as $testDir and $prodDir would be empty when the tests ran. 
    #
    # The apparent exception to this rule is that the tests are done DURING discovery and then stashed for when
    # the tests run. That means your test cases can be set up here if not done directly inside the -TestCases parameter
    #
    Get-ChildItem $prodDir -File | ForEach-Object {
        $fileName = (Split-Path $_ -leaf).PadRight(20, ' ')
        $filePath = $_.PSPath
        It "Vars not declared or changed between function definitions" {
            [System.Collections.ArrayList]$preSource = (Get-Variable).Name
            . "$filePath" -Sourcing
            [System.Collections.ArrayList]$postSource = (Get-Variable).Name
            $postSource.Remove('preSource')
            $postSource.Remove('UnderTest')
            [array]$diff = Compare-Object $preSource $postSource -PassThru
            $diff | Should -BeNullOrEmpty
        }
        It "$fileName all #*, #?, #TODO tags are removed" {
            $_ | Should -Not -FileContentMatch $([regex]::escape('#TODO'))
            $_ | Should -Not -FileContentMatch $([regex]::escape('#*'))
            $_ | Should -Not -FileContentMatch $([regex]::escape('#?'))
        }
    }
}

# Here's a correct example:

BeforeAll {
    $testDir = Split-Path $PSCommandPath -Parent
    $prodDir = Split-Path $testDir -Parent
}
Describe "Describing your tests" {
    # You can still set them up dynamically as test cases
    [System.Collections.ArrayList]$testCases = @()
    Get-ChildItem $prodDir -File | ForEach-Object {
        $fileName = (Split-Path $_ -leaf).PadRight(20, ' ')
        $filePath = $_.PSPath
        $testCases.Add({fileName = $fileName; filePath = $filePath})
    }
    It "<fileName> Exists" -TestCases $testCases { 
        #using <varName> will dynamically name your tests with data
        # from your test cases
        $filePath | Should -Exist
    }
    It "<fileName> all #*, #?, #TODO tags are removed" {
        $filePath | Should -Not -FileContentMatch $([regex]::escape('#TODO'))
        $filePath | Should -Not -FileContentMatch $([regex]::escape('#*'))
        $filePath | Should -Not -FileContentMatch $([regex]::escape('#?'))
    }
    Context "Different Context, Run different tests" {
        $testCases = @{filename = 'file4'.PadRight(20, ' '); filepath = '/path/to/file4.ps1' },
        @{filename = 'file5'.PadRight(20, ' '); filepath = '/path/to/file5.ps1' }
        It "Exists" {
            $filePath | Should -Exist
        }
    }
    Context "You can also set them up inline like this" -TestCases @(
        $testCases = @{filename = 'file1'.PadRight(20, ' '); filepath = '/path/to/file1.ps1' },
        @{filename = 'file2'.PadRight(20, ' '); filepath = '/path/to/file2.ps1' },
        @{filename = 'file3'.PadRight(20, ' '); filepath = '/path/to/file3.ps1' }
    ) {
        It "Run some tests" {
            $fileName | Should -BeOfType [string]
        }
    }

}
Efie
  • 1,430
  • 2
  • 14
  • 34
  • 2
    I was going to comment the same as I was playing around with similar yesterday. Historically I've used a lot of ForEach loops in my tests vs using the TestCases parameter as I started doing it before TestCases was a thing (I think). However as of Pester v5 because of the way it does test discovery it seems this no longer works for all cases, in particular wherever you map a variable to the parameter of a cmdlet. Using TestCases is the correct alternative. – Mark Wragg Jun 23 '20 at 08:33
  • 1
    Edited with some examples – Efie Jun 23 '20 at 13:30
  • At my side $PSCommandPath stays empty with Pester 5. What am I doing wrong? – Carl in 't Veld Oct 02 '20 at 08:39
  • I am so **not** getting Pester 5. Playing around with their own [Foreach](https://pester.dev/docs/usage/test-file-structure#expanding-data-to-generate-describe-and-context-blocks) example, I'm getting a `$files cannot be retrieved error` – Lieven Keersmaekers Mar 12 '21 at 15:28
  • 1
    Post what you're working on in another post and I will try and help you with it. It's been tricky for me too. There aren't a lot of resources for v5 on stack overflow yet so answering some of these questions where others can see would probably be useful. – Efie Mar 12 '21 at 15:33
  • Sorry I didn't realize you said you were just using the example. What version of 5 are you on? They didn't add -Foreach support until 5.1 – Efie Mar 12 '21 at 15:37
  • How are you invoking the tests? You can change the output verbosity using the legacy param -Output Detailed or -Output Diagnostic. If you're using the new preferred method with a configuration object you can set the $config.Output.Verbosity to Detailed – Efie Mar 12 '21 at 16:59
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/229834/discussion-between-efie-and-lieven-keersmaekers). – Efie Mar 12 '21 at 17:13