-
-
Notifications
You must be signed in to change notification settings - Fork 474
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
improved suport for pipeline processing with begin block #422
Conversation
oops. will look into failures tonight. They all passed on my box :) |
@dlwyatt is it possible that TC is not calling |
Try adding it to the module manifest as well. (Come to think of it, I'm not sure why we bother to use Export-ModuleMember at all, but whatever. :) ) |
Ahh right! Thanks man. That makes total sense. |
ok. This is much more in line with failures I would expect - broke PSv2. Will get that fixed later. |
OK. v2 (and others) are passing now. Its ready for review. |
I'm trying to wrap my head around what problem this solves. In the current version of Pester, Mocks don't have begin blocks or end blocks, and I can't see a reason why it should matter whether the original function does or doesn't, since you're mocking it anyway. The one situation where this might get goofy is if you use a parameter filter on the mock, and the filter matches for some of the input objects but not others (causing the original command to be called several times in the same pipeline, instead of once.) Do you have an example of tests that aren't working the way you'd like them to without this change? |
Sorry for being too abstract and not making my exact problem more clear. The issue is not about allowing mocks to have In my case I am mocking
Now this does not have an explicit I am upgrading to the latest Pester from version 2.2. In 2.2 I did not run into this because it handled scoping differently. I could have changed Does this make sense? Have a look at the function I created for the tests I added. The tests call the function before it is mocked then mock it and set a ParameterFilter to avoid the mocked behavior so the wrapped original implementation is invoked and then assert that the parameters are bound the same and return the same values and the begin block is initialized just as it is without the mock. Without the new Mock code, those tests would show the begin block being called for each value in the pipeline resetting the variable set there. I realize this increases the complexity of mock.ps1 which I am not crazy about and I respect your judgement on whether to introduce the added complexity or not. I learned some things I did not know about parameter binding and pipeline flow through working on this. |
OK, that makes more sense. I'll play around with the code in the PR with that in mind. :) If I have any ideas for making it work with less complexity, I'll let you know. |
$result = @(1,2) | PipelineInputFunction | ||
it 'Returns actual implementation' { | ||
$result[0].keys | % { | ||
$result[0][$_] | Should Be $noMockArrayResult[0][$_] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While these loops do result in fewer lines of code, the output of a failed test is less useful (since we'd be seeing $_ in the logs). Would it be worth it to just explicitly check each property of your function's hashtable? Lots of duplication, but much clearer failure messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup. I agree.
Brainstorming a bit on a possible alternate approach:
I think with that approach, we should be able to solve the problem ( so we only call the original command zero or one times ), without needing the overhead or complexity of Select-NonPipelinedBoundParameters. |
Thanks @dlwyatt I will give this a shot. |
I was just looking over your suggestion again and I'm not sure this gets around the need for |
Are there still pipeline-bound parameters when the End block runs? If so, we can just cache a copy of $PSBoundParameters as it existed when the Begin block ran. Calling Invoke-Mock from all three blocks gives us lots of flexibility. |
Maybe I'm misunderstanding a fundamental truth (or more) regarding parameter binding. I have not been as entrenched in the powershell world as I once was. My understanding is that the binding occurs once and not separately per block. At any rate, I'll experiment and report back. Thanks! |
As I understand it, pipeline binding only takes place in the process block (and takes place multiple times, if there are more than one input object.) You can see this in action with Trace-Command. |
Maybe I'm misunderstanding a fundamental truth (or more) regarding parameter binding. I have not been as entrenched in the powershell world as I once was. My understanding is that the binding occurs once and not separately per block. At any rate, I'll experiment and report back. Thanks! |
Going to take a shot at this tonight, see what happens. I've pulled your Mock.Tests.ps1 file as-is, and will try to make the tests pass using the approach I was thinking of. If it works, we can compare notes :) |
oh great. It was on my list of stuff to do for the weekend but let me know what you find. |
Appears to work, though I've only tested PSv5 and PSv2 on my computer; it won't go through the build server unless it's part of a branch or pull request in the main repo. Invoke-Mock was overdue for quite a bit of refactoring, and I did some of that to make the new code clearer. |
Beautiful! Works with my boxstarter unit tests too. |
This fixes scenarios where a mocked function is an advanced function and initializes state in a
Begin
block. Here is an example:Currently, if pester mock the above function and the function is called piping an array as input, the
Begin
block will be called for each item in the array.This PR fixes this.
First, it changes the mock prototype to be wrapped in an
End
block and batches any pipeline input into an array. Then it filters out all bound parameters that were bound from the pipeline and finally calls the mock (or the original function if the parameter filter does not pass) with the original calling pipeline and bound parameters.This definitely got more hairy than I would have liked. was really hoping NOT to have to written the
Select-NonPipelinedBoundParameters
function but could find no invocation metadata getting me what I needed. In the event that the parameter filtering breaks, I have pester fall back to the original non pipelining call and I'm hoping all the tests catch the edge cases.