pester / Pester

Pester is the ubiquitous test and mock framework for PowerShell.
https://pester.dev/
Other
3.11k stars 473 forks source link

$PSBoundParameters and copies of it get permuted when used by Pester #2093

Closed tbergstedt closed 1 year ago

tbergstedt commented 3 years ago

General summary of the issue

Describe your environment

Pester version : 5.3.0 C:\Program Files\PowerShell\Modules\Pester\5.3.0\Pester.psm1 PowerShell version : 7.1.4 OS version : Microsoft Windows NT 10.0.18363.0

Steps to reproduce

I have a set of Pester scripts that I want to be able call all at the same time using a wrapper script. The idea is that the developers should have the possibility to execute them individually, or all in one go. The actual test scripts have a number of in parameters, mutually exclusive via parameters sets that defines how they should be run, so naturally I'd like to use $PSBoundParameters within the wrapper script, then setting each Pester run up with a test container.

Test script(s), Assert-X.ps1

#Assert-1.ps1
param(
    # Pull Request Id. 
    [Parameter(Mandatory,ParameterSetName='ByPullRequestId')]
    [string]$PullRequestId,

    # Id of Commit(s)
    [Parameter(Mandatory,ParameterSetName='ByCommitId')]
    [string[]]$CommitId,

    # Switch for including all items in directory search, and not just changed items
    [Parameter(ParameterSetName='ByDirectory')]
    [switch]$Full
)
...

Wrapper script

    $scripts = Get-ChildItem "$PSScriptRoot\Assert-*.ps1"
    $testContainer = New-PesterContainer -Path $policyScripts -Data $originalParameters
    $testConfiguration = New-PesterConfiguration @{
        Run = @{
          Container = $testContainer
        }
        Should = @{
            ErrorAction = 'Continue'
        }
    }
    Invoke-Pester -Configuration $testConfiguration

Expected Behavior

Each script executes using the same set of parameters and executes. (I'm not sure if the output would be exactly like below since I haven't been able to run them. But I hope you get the idea.

Starting discovery in 3 files.
Discovery found 3 tests in 1.26s.
Running tests.
[+] C:\git\Assert-1.ps1 1.69s (421ms|61ms)
[+] C:\git\Assert-2.ps1 1.70s (422ms|62ms)
[+] C:\git\Assert-3.ps1 1.71s (423ms|63ms)
Tests completed in 1.74s
Tests Passed: 9, Failed: 0, Skipped: 0 NotRun: 0

Current Behavior

After only one use of $PSBoundParameters, it gets permuted with key-value pairs from the previous run. Even if for example only -PullRequestId was given as input the first time, -CommitId is included with the in-parameters with a $null value, causing the subsequent runs to fail:

[-] Discovery in C:\git\\Assert-2.ps1 failed with:
*System.Management.Automation.ParameterBindingValidationException: Cannot bind argument to parameter 'CommitId' because it is null.*
Discovery found 9 tests in 1.31s.
Running tests.
[+] C:\git\Assert-1.ps1 2.6s (1.28s|106ms)
Tests completed in 2.69s
Tests Passed: 9, Failed: 0, Skipped: 0 NotRun: 0
*Container failed: 2
  - C:\git\Assert-2.ps1
  - C:\git\Assert-3.ps1*

I have tried various methods for going around the problem:

    # Alternative 1: use psobject.copy()
    $originalParameters = $PSBoundParameters.psobject.Copy()

    # Alternative 2: Build a parameter set from scratch based on $PSBoundparameters
    $commonParameters = @{}
    $PSBoundParameters.Keys | ForEach-Object {
        $commonParameters.$_ = $PSBoundParameters.$_
    }

But these values get permuted as well.

To be fair, I'm not entirely sure this is a problem with Pester, since I'm not that familiar with $PSBoundParameters. But I have seen that the permutation only occurs through the use of Pester, any variable created like above not being used in Pester stays unchanged. And it seems to affect any variable used.

nohwnd commented 3 years ago

I think this is the same problem: https://github.com/pester/Pester/issues/2073

We plan to add clone which will do shallow clone (only clone the hashtable, but not clone the values in it).