Hi there! While writing this many of you were attending PSConfEU in Prague. Unfortunately I couldn’t join this year so instead I decided to write my first PowerShell-related post. Luckily we just got a question in the Pester-repo from @tahasi that I found interesting.
Is there a recommended way of creating a Pester mock that has a different result for sequential calls?
The user provided a working mock but felt it could be improved.
While this works just fine it has some obvious drawbacks:
Much of code is used to track the mock’s state, specifically how many times it’s been called
The mock would become large with many possible results
Duplicate code if multiple mocks share the same pattern
Syntax could be simplified. The user imagined something like:
This is too specific to be a parameter in Mock itself. I suggested writing a helper-function to generate these types of mocks and got inspired to try it out myself. Let’s go through the process together.
First we need to define some requirements based on the question and provided code:
The mock should return a single value per call
The mock should throw an exception when the list of values is exhausted
Multiple mocks should not conflict with each others results or call counter
Let’s begin by creating a function which will generate the scriptblock used by our mock. The list of results are turned into a mandatory array-parameter and the if-statements are replaced by an array lookup to make it short. Now we got this:
The code above meets the first two requirements, but it only works for a single mock. Do you see why?
Multiple mocks in a file would all use the same hard-coded variables to store both the results and call counter.
To fix this we could generate random variable-names for each mock, or use a shared hashtable where each mock would have its own unique key. This time we’re skipping both and will instead use the opportunity to look at closures. Here is an updated function:
Notice that most variable references no longer specify the script-scope. The variables are still hard-coded but that’s no longer a problem. How?
Closures are created by invoking the .GetNewClosure()-method on a scriptblock. It will basically create a copy of the scriptblock and all local variables you’ve referenced inside it and place them in a new dynamic (in-memory) module. In return you get the new scriptblock which is bound to the new module.
The dynamic module looks something like this:
If you import the module above you’d get the same result as one of the closures. The scriptblock will be available in your session using $mockScriptBlock. Because the module only exports the scriptblock, all the variables are only visible inside the module where only the scriptblock is executed.
So by mainly adding .GetNewClosure() in our function, each generated mock will get their own private variables. No more conflicts!
With the updated function we can create mocks like this:
This is great, but maybe we could wrap Mock to make it a single statement?
To fully combine both commands we need to extend our function with the parameters supported by Mock so we can pass them along. It also makes sense to rename the function.
Now we’re ready to try this out.
Success! 🎉
If you need this in multiple files you can place the function in a ps1-file and just dot-source it in each test-file like BeforeAll { . "$PSScriptRoot/TestHelpers.ps1" }.
In this post we made a function to make it easier to create Pester mocks that share the same type of behavior. We’ve also shown one scenario when using closures, a hidden gem in PowerShell, can be really useful.
Mocking could get complicated so in general I’d avoid helpers like this, but sometimes they do make sense and could save you a lot of time and duplicate code.