1

I have a PowerShell function that unfortunately has several dependencies, so when testing I need to setup several different mocks. As I have several tests with different scenarios and some but not all share similar setup activities, there ends up being a lot of duplication between the tests. I want to reduce duplication in my tests by moving the declaration of the mocks into helper functions, eg SetupValidStateForUser

I can declare Mocks from functions without issues, but as soon as I introduce a Mock that uses a -ParameterFilter that uses an argument from the helper function it looks like the passed-in parameters to my helper functions are not in scope when the Mocks are resolved. How can I adjust the mock declaration so that my arguments will be resolved correctly?

I'm using Pester v5 with PowerShell 7.

Here's a test that shows the failing scenario:

Context "Setting up a Mock from a Function" {

   BeforeEach {
      
      Function SubjectUnderTest {
         Param(
            [string]$Arg1
         )

         return $true
      }

      Function Setup-MockForSubjectUnderTest {
         Param(
            [string] $ExpectedArg,
            [bool]   $ReturnValue
         )

         Mock SubjectUnderTest `
                   -Verifiable `
                   -ParameterFilter { $Arg1 -eq $ExpectedArg } ` # <-- scope issue?
                   -MockWith { $ReturnValue }
      }
   }

   It "Should allow me to setup a mock with parameters from a helper function" {
      # arrange
      Setup-MockForSubjectUnderTest -ExpectedArg "hello" -ReturnValue $true

      # act
      $result = SubjectUnderTest -Arg1 "hello"

      # assert
      $result | Should -Be $true
      Should -InvokeVerifiable # <-- fails
   }

}
bryanbcook
  • 16,210
  • 2
  • 40
  • 69

1 Answers1

2

Yes, it is a scoping issue, so you could create the variables in an accessible scope like this:

Describe "Test" {
    Context "Setting up a Mock from a Function" {
        BeforeEach {
            Function SubjectUnderTest
            {
                Param(
                    [string]$Arg1
                )

                return $false
            }

            Function Setup-MockForSubjectUnderTest
            {
                Param(
                    [string] $ExpectedArg,
                    [bool]   $ReturnValue
                )

                New-Variable -Name "mockExpectedArg" -Value $ExpectedArg -Scope "script" -Force
                New-Variable -Name "mockReturnValue" -Value $ReturnValue -Scope "script" -Force

                Mock SubjectUnderTest `
                    -Verifiable `
                    -ParameterFilter { $Arg1 -eq $mockExpectedArg } `
                    -MockWith { return $mockReturnValue }
            }
        }

        It "Should allow me to setup a mock with parameters from a helper function" {
            # arrange
            Setup-MockForSubjectUnderTest -ExpectedArg "hello" -ReturnValue $true

            # act
            $result = SubjectUnderTest -Arg1 "hello"

            # assert
            $result | Should -BeTrue
            Should -InvokeVerifiable
        }

        It "Should allow me to setup a mock with parameters from a helper function" {
            # arrange
            Setup-MockForSubjectUnderTest -ExpectedArg "bye" -ReturnValue $false

            # act
            $result = SubjectUnderTest -Arg1 "bye"

            # assert
            $result | Should -BeFalse
            Should -InvokeVerifiable
        }
    }
}

But also try to use TestCases (see Pester docs for more info), for example:

It "Given valid -Name '<Filter>', it returns '<Expected>'" -TestCases @(
    @{ Filter = 'Earth'; Expected = 'Earth' }
    @{ Filter = 'ne*'  ; Expected = 'Neptune' }
) {
    param ($Filter, $Expected)

    $planets = Get-Planet -Name $Filter
    $planets.Name | Should -Be $Expected
}
mhu
  • 17,720
  • 10
  • 62
  • 93