Mocking Missing Cmdlet Pipelines with Pester

Following on from the previous the Mocking Missing Cmdlets with Pester and Mocking Missing Cmdlet ErrorAction with Pester posts, I also encountered (yet) another interesting problem when attempting to mock cmdlets that were not present on the test system. This one is pretty similar to the -ErrorAction edge-case but involves the pipeline. Here’s a pseudo test that was failing:

In the above example, we wish to mock both the Get-VM and Remove-VM cmdlets and assert that the Remove-VM cmdlet is called. If we run this on a system that does not have the Hyper-V cmdlets installed we receive the following error:

The error message is fairly self-explanatory. Just like your standard Advanced Function definition, we need to indicate that a parameter needs to be able to accept input via the pipeline using the ValueFromPipeline attribute. To fix this we just need to add the Parameter attribute to our stub function definition:

Whilst it’s not rocket-science – just in case someone else runs into it – I thought it would be worth quickly documenting! Here’s the working example:

 

Mocking Missing Cmdlet ErrorAction with Pester

Following on from the previous Mocking Missing Cmdlets with Pester post, I also encountered another interesting problem when attempting to mock cmdlets that were not present on the test system. This one is more of an edge-case, hence its own post. Just like last time, the tests worked when I had the Hyper-V cmdlets installed, but failed when running within an Appveyor VM.

It’s probably not uncommon that you will need to ensure that the code under test should throw an error here-and-there. Here is a pseudo-example that tests that Get-VM writes an error when passed with a non-existent VM name:

When this test is run it fails:

The problem here is that the stub function is not an advanced function and the –ErrorAction preference switch is ignored! This is easily resolved by adding the [CmdletBinding()] attribute to the stub function definition:

Running the test now results in the expected output:

This is not a Pester issue and it’s not a Powershell issue either. It’s just the way the normal Powershell functions work. But, just in case someone else runs into it I thought it would be worth quickly documenting!

Mocking Missing Cmdlets with Pester

When writing Pester unit tests for your Powershell code you will probably have a need to mock calls to external functions before too long. This process works as you would expect when Pester can locate a defined function/cmdlet with a matching name. However, if Pester cannot find a definition, it will fail.

This problem will normally surface in a Continuous Integration (CI) environment. For me, it was writing the first suite of tests for the xHyper-V DSC resource module. The Hyper-V cmdlets where present on my authoring machine but were not present on the Appveyor build VM.

Here is an overly simplified Pester test that I’ll use for demonstration purposes:

If we run the test and Pester cannot locate a defined function then it will report an error. Note: if you run this on a machine with the Hyper-V module installed (and you have a VM called ‘TestVM’) then it will pass – but you knew that already ;).

This is easily overcome by defining an empty function within the test file. Note: this will need to be defined within the ‘InModuleScope’ script block if you’re testing a module’s internals.

The test now passes as we would expect. Yay \o/

Now, what you really need to know is that for Pester to enumerate and mock parameter filters, those parameters need to be defined on the stub function. If we were to update the test to check for the passing of a particular –Name parameter like so:

When we run the test it will now fail again.

For Pester to enumerate the dynamic parameters on the function, it needs to have the parameters (only the one’s you’re interested in) defined. This can easily be fixed like so:

The tests will now once again pass successfully!

Hopefully this helps someone and saves some time. It took me a while to work out what was going on as I had the cmdlets available on my development machine but the tests were failing when running in an Appveyor VM. Perhaps I should submit a pull request to get this put into the Pester help documentation?!

Testing Private Functions with Pester

We’ve been busy beavering away on a new Powershell module that is comprised of many .ps1 files, that are loaded by a master/control .psm1 file when the module is imported. And, like all good Powershell citizens, we have many Pester unit tests for this code in accompanying .Tests.ps1 files.

As Powershell script modules permit us to control which functions should be exported with Export-ModuleMember and Pester allows us to test non-exported functions with the InModuleScope option, you might be wondering why we would ever need to be able to test private/internal functions?

Script Bundles

Whilst coding the new Powershell module, we have always had the desire to ensure that it could also be used as a ‘bundled’ .ps1 file. By bundle, we mean a single combined .ps1 file that can be included verbatim at the beginning of an existing script or by dot-sourcing it as required. Unfortunately – in this particular scenario – the internal module functions would be exposed and could potentially cause unnecessary confusion.

Here’s an example where both the ‘PublicFunction’ and ‘InnerPrivate’ functions would be exposed when bundled or dot-sourced into an existing .ps1 file.

If we only want the ‘PublicFunction’ visible then the simple solution to this is to nest the private function(s) inside the public function(s) like so:

Now only the ‘PublicFunction’ will be available. End of the story?

Internal Functions

Not quite; by hiding the functions we now cannot test them with Pester. I had a very brief conversation with Dave Wyatt on GitHub about this and it was agreed that this functionality should not be a part of the official Pester release.

To solve this particular issue, we have a simple function that will locate and return a function’s definition from within a .ps1 file as a script block. The function definition can then be dot-sourced into the current Pester scope to enable testing. Here’s an example Pester test file that will test our ‘InnerPrivate’ function:

If this code is of interest you can simply save the following code as a .ps1 file in Pester’s \Function directory, for example \Functions\FunctionDefinition.ps1, and Pester will automatically load it.

Notes:

  1. As this code utilises the Abstract Syntax Tree (AST) it does require Powershell 3.0;
  2. If there are any dependencies on variables in the function’s parent scope these will need to be mocked/accounted for;
  3. Depending on how you install/update the Pester module, this might get overwritten when Pester is updated.

When we have more time we’ll put this up on Github so people can collaborate on changes. In the meantime, here’s a copy of the function.

Archives

Categories