Pester
Pester copied to clipboard
Better way to evaluate that a mock was called with specific parameter value
Checklist
- [X] Feature request has a meaningful title
- [X] I have searched the existing issues. See all issues
- [X] I have tested using the latest version of Pester. See Installation and update guide.
Summary of the feature request
As a user I am often validate if mocked command was called with the expected parameter value. It often goes like this:
It 'Should call the mock with the expected parameter value' {
Mock -CommandName Get-MyInternalCommand
Get-Something # Calls Get-MyInternalCommand internally
Should -Invoke -CommandName Get-MyInternalCommand -ParameterFilter {
$MyParameter -eq 'evaluate string value'
}
}
If this fails it returns the unspecific error
Expected Get-MyInternalCommand in module X to be called at least 1 times but was called 0 times
How should it work?
Instead of the above and since we already have $PesterBoundParameters it would be great if we could get Pester to return a more specific error.
If we could get this to work:
It 'Should call the mock with the expected parameter value' {
Mock -CommandName Get-MyInternalCommand
Get-Something # Calls Get-MyInternalCommand internally
Should -Invoke -CommandName Get-MyInternalCommand -UsingParameter 'MyParameter1'
Should -Invoke -CommandName Get-MyInternalCommand -UsingParameter 'MyParameter2' -WithValue 'evaluate string value'
}
If could then return more specific errors:
Expected Get-MyInternalCommand in module X to be called using the parameter MyParameter1 at least 1 times but was called 0 times.
Expected Get-MyInternalCommand in module X to be called using the parameter MyParameter2 with the value 'evaluate string value', but it was called with the value 'something else'.
Thanks for the feedback!
Because we can't use parametersets in Should-operators, it might be confusing to see ParameterFilter and two new shortcut-parameters. Listing called values could also get messy quick with multiple calls.
Maybe include the expected value in the testname? There's also #2121 to fix -Because for -Invoke* which I'd prioritize.
I've submitted a PR to enable -Because with -Invoke. Let me know if you don't consider that as a solution.
It wouldn't be as clean, but I think it is better than how it works today (since it does not work at all today). 🙂
Would it be hard to make the parameter variables or $PSBoundParameters work in Because so it possible to do below?
Should -Invoke -CommandName Get-MyInternalCommand -ParameterFilter {
$MyParameter1 -eq 'string1'
} -Because "expected the parameter MyParameter1 to have the value 'string1', but the actual value was <PSBoundParameters.MyParameter1>"
if not then it would not be optimal since it could not output the actual value which is important to know in this scenario.
Should -Invoke -CommandName Get-MyInternalCommand -ParameterFilter {
$MyParameter1 -eq 'string1'
} -Because "expected the parameter MyParameter1 to have the value 'string1'"
Telling what the expected value should be is good, but then we still don't know what the value actually was, or was it $null. We still have to add a Write-Verbose -Verbose -Message $MyParameter1 to know what the actual value is (or do step debug) to understand mor why the test failed.
Running with Diagnostic output will tell you the parameters used for each call when mock is invoked for troubleshooting.
Resolving the variable in -Because won't work well unfortunately. Should -Invoke evaluates the call history (multiple), not an individual mock call.
Printing the context (parameters) like we do in Diagnostic for all calls to the same CommandName+ModuleName is an option. It would get really noisy when parameters has longer values or complex objects, so not sure if I'd prefer a -ErrorIncludesCallHistory type of switch for that or simply refer to debug-output.
You can use Detailed output with only debug-logging for Mock to reduce some of the noise (Mock debug itself is talkative). @nohwnd ?
Related #376
Resolving the variable in -Because won't work well unfortunately. Should -Invoke evaluates the call history (multiple), not an individual mock call.
I guess we could dot source the filter and evaluate the because string after the filter finished running. We do that for the test name expansion.
Is this going to be a request that could pop up for Mock as well? Because there we would not know if we should evaluate in filter or the mock body.
The root cause is that we cannot have parameter sets in the current Should so we can add parameters that are intuitive 🤔
What I understood by comments in other issues is that the goal is to move to something like Should-Invoke which can have parameter sets. Is that correct? In that case then I suggest we settle with the change that @fflaten done in the PR and wait to make this better in the future?
I was commenting just on the quoted remark: Expanding is not possible for parameter filter. And now I see that I probably missed the mark and it would not be easily possible.
So yeah let's go with what frode implemented.
I just tried this in a test of mine, and it seems to work pretty well. Is there any downside to this?
It 'Should call the mock with the expected parameter value' {
Mock -CommandName Get-MyInternalCommand
Get-Something # Calls Get-MyInternalCommand internally
Should -Invoke -CommandName Get-MyInternalCommand -ParameterFilter {
$MyParameter | Should -Be 'evaluate string value'
$true
}
}
Is there any downside to this?
Not able to test atm. but I think it breaks Should.ErrorAction = 'Continue' if you use that. See this test
Also, see #1712