Search code examples
powershellpester

PowerShell Pester : Use a variable in multiple It-blocks and recognize function when invoked from PowerShell Window


I have the following Pester Test-script:

function Get-Name{
    return "TestObject"
}

Describe{
    BeforeAll{
        $Script:ObjectID
        $API::[API]::New()
    }

    It 'Get ObjectID By NAME'{
        $Name = Get-Name
        $Object = $API.GetObjectByName($Name)
        $ObjectID = $Object.id
        $ObjectID | Should -BeGreaterThan 0
        Write-Host "ObjectID = " + $ObjectID
    }

    It 'Print Object ID'{
        write-host $ObjectID 
    }
}

My issue is the following:

Issue 1: Function not recognized when the script is 'invoked'.

Whenever I run the script with the Invoke-Pester -Path c:\path\test.ps1 -Detailed -command, my first test fails due to the function not being recognized. However, when I compile and run my script in PowerShell ISE, the first test succeeds.

Issue 2: Pass ObjectID to another It-block My second test never outputs the ID retrieved from the first test. I do not know how to pass the variable from one test, to another. I already have tried putting the BeforeALl above the Describe but without success.

What am I doing wrong in both cases? My pester version is 5.6.1


Solution

  • For issue 1, the most commonly used option is to have a psm1 in the same folder as your tests scripts where you have all the functions needed for them, for example:

    • common.psm1
    function Get-Name {
        return "TestObject"
    }
    
    # Other functions used in all Pester Tests
    

    Then in your .tests.ps1 file you can use Import-Module:

    $common = Join-Path $PSScriptRoot -ChildPath common.psm1
    Import-Module $common
    
    Describe .... {
    
    }
    

    The other option is to use a scope modifier if you want to have the function in the same file as the test. See about Scopes for details.

    function global:Get-Name {
        return "TestObject"
    }
    
    Describe .... {
    
    }
    

    For issue 2, as stated in comments, It blocks should be isolated from one another, you should ideally first assert your Get... works then, in your next test and since you have already asserted it worked, you can Get... again and then assert Update... works, and so on. Of course this might slow down your tests but the point is to ensure it works not that it is fast.

    This is an example of a commonly used pattern:

    function script:Get-Name {
        return 'TestObject'
    }
    
    Describe 'My Module' {
        Context 'Get Thing' {
            It 'Get Object' {
                Get-Name | Should -Be 'TestObject'
            }
        }
    
        Context 'Do stuff that depends on Get Thing' {
            BeforeAll {
                # now that we asserted that Get works,
                # we can use it for next tests:
                $foo = Get-Name
            }
    
            It 'Update Object' {
                $foo + ' hello' | Should -Be 'TestObject hello'
            }
    
            It 'does other stuff with `$foo`' {
                # ...
            }
        }
    }
    

    If you must pass an object from one block to the other you can, again, use a scope modifier:

    function script:Get-Name {
        return 'TestObject'
    }
    
    Describe 'Some tests' {
        It 'Get Object' {
            Get-Name | Should -Be 'TestObject'
            $script:foo = Get-Name
        }
    
        It 'Update Object' {
            $foo + ' hello' | Should -Be 'TestObject hello'
        }
    }