pester / Pester

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

Provide a way to more easily modify a mocked parameter before comparing with a test value. #2420

Closed Spoonsk closed 8 months ago

Spoonsk commented 8 months ago

Checklist

Summary of the feature request

I am writing test cases for a module which makes api calls and some of those API calls have bodies that are passed into them.

Originally I was doing JSON string compare to see if the body passed into my mock had the expected result:

Should -Invoke -CommandName Invoke-HATApiMethod -ModuleName $ModuleName -Times 1 -ParameterFilter {
        $Body -eq $expectedBody.trim()
      }

These tests were a bit flakey as sometimes the JSON generates with slight differences. To get around that, I opted to do a deep object compare. I wanted to use -ParameterFilter for this initially, however I was unable to modify or reassign $Body variable within this script block. Doing so would give me a null value. For example:

Should -Invoke -CommandName Invoke-HATApiMethod -ModuleName $ModuleName -Times 1 -ParameterFilter {
        $Body.trim() -eq $expectedBody.trim() # This does not work, $Body in this context will be null
      }

My actual end goal is to not string compare at all, rather I want to convert the JSON to an object and do a deep compare of the objects. I was able to do this with the following code, however I feel like this is just begging to be broken by a pester framework change (the deep compare code is an internal function, however its implementation is irrelevant to my issue).

      # Pull the body param passed into Invoke-HATApiMethod from pester's call history
      $mockedBody= $____Pester.CurrentTest.PluginData.Mock.CallHistory['mymodule||Invoke-HATApiMethod'].BoundParams.Body
      # Deep compare expected json with tested json
      $jsonCompare = Compare-HATDeepObject -Ref ($expectedBody | ConvertFrom-Json) -Diff ($mockedBody | ConvertFrom-Json)
      $jsonCompare | Where-Object {$_.Match -ne '=='} | Should -BeNullOrEmpty

I'd like a way to manipulate a mocked parameter before testing it.

How should it work?

This could be done by:

  1. Adding a function to pull this info, something like Get-MockedParameters which could return an array of the current mock call history with the most recent call in position 0
  2. Modifying parameterFilter so that you can manipulate the parameter values (not sure how feasible this is)
  3. Exposing a variable that is easier to access for accessing the mocked parameters.
fflaten commented 8 months ago

Hi. Are you sure you haven't also called Invoke-HATApiMethod without a -Body value?

-ParameterFilter will loop through all invocations in the same scope (test), so if one call was made without -Body it will fail unless you null-check in your paramterfilter.

That's the main difference between it and your workaround which use member enumeration (...ApiMethod'].BoundParams.Body) as this will silently skip the calls without a Body value.

Spoonsk commented 8 months ago

This is exactly what was happening. Thanks for the quick reply! This issue can be closed.

The following worked for me:

      Should -Invoke -CommandName Invoke-HATApiMethod -ModuleName $ModuleName -Times 1 -ParameterFilter {
        # We do not want to filter for the first call of this function, which doesn't contain the body param
        if($null -eq $Body){
          return $false
        }
        $MockedBodyObj = $Body | ConvertFrom-Json
        $Diffs = (Compare-HATDeepObject -Ref $MockedBodyObj -Diff $expectedBody -) | Where-Object {$_.Match -ne '=='}
        return $null -eq $Diffs
      }
fflaten commented 8 months ago

Glad I could help. 🙂 Thanks for using Pester!