DSCR and Pester testing

In my previous post DSCR for VMware and you! I described the open source project that was started to develop DSC resources for vSphere (DSCR). One of the requirements for contributing, is that you provide Unit and Integration Pester tests for any new DSC resource you contribute to DSCR.

The vSphere environment and VMware PowerCLI have some peculiarities that will require you to use some specific Pester techniques to write these Pester tests. This post should help you understand how this done. This is not intended as a Pester course, there are other, and better, resources for that.

Some Basics

Describe + It

Since most of us are not regular or experienced Pester test writers, I will start with some basic concepts, before diving into more vSphere specific techniques. Just follow along.

From the Pester Wiki we learn that ”
Pester is a Behavior-Driven Development (BDD) based test runner for PowerShell “. One of the guiding principles in BDD is ”
… tests of any unit of software should be specified in terms of the desired behavior of the unit“.
In short, we are going to write tests to verify that our code behaves as we expect it to behave, in all possible circumstances. And these tests will also allow us to verify that any future changes to the code do not break it.

In its most basic format, a Pester test consists of a Describe block, a way of logically organising tests, and an It block, in which we assert a test. The word assert comes from the Arrange-Act-Assert pattern.

Time to see some code. The following is a very simplistic test setup.

We are testing a function Do-Something. For that we start a Describe block. In that block, we have one It block. And in that It block we assert that our function returns what we expect it to return.

In this test, which succeeded, we verified that our Do-Something function actually returned the text we expected.

If we test for another return value

we get a failed test.

Mocking

In the previous examples we actually called our function. Executing the function did not do anything, it did not change anything in our environment. But not all functions or cmdlets are harmless like that.

For those types of functions and cmdlets we “mock” the real function/cmdlet.

In this example we introduce two new Pester commands. The Mock command and the Assert-MockCalled command.

With the Mock command we “…
specify a script block that will become the command’s new behavior “. And Assert-MockCalled “…
verifies that a mocked command has been called a certain number of times“.

Context

We already saw the Describe block as a way of keeping related tests together, but we go one level deeper with the Context command. It ”
Provides logical grouping of It blocks within a single Describe block“.

Which results in

Notice how the test results follow the subdivision we created with our Describe and Context blocks.

Tags

A final feature I want to mention in this (very) basic Pester intro, is the concept of using Tags. On the Describe command, you can use the Tag parameter. These tags are a kind of label. When you run the Pester tests (with Invoke-Pester), you can specify wich tests shall be executed by specifying tags.

When we run this by just doing

we get

but if we use the Tag parameter, we can do

and that results in

PowerCLI

Single Cmdlet

As we already saw earlier, we can simulate the behaviour of cmdlets with the Mock command. Since we do not want any PowerCLI cmdlets we are using, to actually go out to a real vSphere environment, which might not even be there, we are going to use the Mock command.

The function calls the PowerCLI cmdlet Get-VMHost and were mocking that cmdlet with an empty scriptblock. The result is similar to what we see earlier.

But we probably also want to check if our code is passing the correct parameters to the Get-VMHost cmdlet. That is where the ParameterFilter on the Mock command comes into play.

Which results in

If we call the function with another value for the Name parameter, our test will fail.

which results in

What we are seeing here is interesting to further analyse. Our mock for Get-VMHost was not taken due to the ParameterFilter we used. So that is working as desired.

But as a consequence, the test now used the “real” Get-VMHost, which we can see by the fact that the cmdlet complains there is no connection.

One way of avoiding that is by creating a kind of catch-all Mock.

We do get a failed test, but at least our testing is not calling the real cmdlet anymore.

Cmdlets in a Pipeline

We often use the pipeline to link two cmdlets together. Testing such a construct only requires adding additional mocks.

That was easy.

Mocking the Module

But what if there are conditions on properties. For example a Where-clause, in the pipeline construct that tests if a VMHost has four or less CPUs? How do we test if our code is handling that correctly in all possible scenarios.

We need to be able to mock the output of the Get-VMHost cmdlet. Unfortunately, the way PowerCLI types are currently set up, we can not just create new PowerCLI objects and assign values to their properties. In fact there is a PowerCLI Idea open to change that behaviour.

The solution that DSCR has chosen is to create a mock module VMware.VimAutomation.Core. This is the module where most PowerCLI types are defined.

If we replace that module with our own module, we can completely manipulate PowerCLI objects. And we can make sure that our Pester tests use that module, by explicitly importing it in our test script.

For the test scripts in this post I used a very simple setup.

In the mock module VMware.VimAutomation.Core we have the following content.

Annotations

LineAnnotation
1-15Use the Add-Type cmdlet to create the class VMware.Vim.VMHost with the properties we require, i.e. NumCpu
2All classes are defined in the VMware.Vim namespace
17-24Define our version of the Get-VMHost cmdlet
26-35Define our version of the Get-VM cmdlet
30-31Define the Location parameter to accept pipeline values of type PSObject.

Our test script now imports our mock module. This allows us to specify exactly what the Get-VMHost cmdlet is going to return via a hashtable that we use the define the content of the object that is returned by our Get-VMHost mock.

And since we defined what kind of parameter our Get-VM cmdlet accepts over the pipeline, we can combine our cmdlets in a pipeline construct.

The results in

Notice how I used the Verbose switch to show where our mock module is coming from.

Now that we have the pipeline construct working we can add test cases for multiple possibilities.

This is a good moment to introduce different Context blocks for different test cases. We will do one context for an ESXi node with 4 CPU and another context for an ESXi node with 8 CPU.

Notice how have test for zero calls to Get-VM in the second context. Due to the Where-clause that VMHost object should not get through. The result

If you look at the ParameterFilter on the Get-VM mock in the first context block, you will notice that we test a specific property of the VMHost object. Would it not be better to be able to test on the complete VMHost object?

Of course it would, but for that we need to adapt our VMHost class in our mock module. We need to add the IEquatable interface to the class. That adds to our class the ability to determine if two instances of the class are identical.

And we can adapt the ParameterFilter.

The result of a run looks the same, with the difference that we now compared the complete VMHost object.

vSphere

Get-View and ExtensionData

We all know that sometimes we have go into the vSphere API to retieve properties or to call methods that are not exposed directly through any of the PowerCLI cmdlets.

The code behind DSCR is no different. But how will we test access to these vSphere API properties or calls to any of the vSphere API methods?

Turns out that this is not very different from what we already did for the PowerCLI cmdlets. We define the vSphere API objects in our mock module. And we can mock the Get-View cmdlet and the ExtensionData property.

In the following example we have code that looks at the current EVCMode ofan ESXi node, and then returns that mode in a more human readable format. Nothing too fancy.

Since we are fetching values under ExtensionData we will have to define the required classes and properties in our mock module. We add the HostExtensionData and HostListSummary classes to our module mock.

Note that we only added the properties that are required for this test case. When more test cases are created, the class definitions will be expanded.

Our test script looks like this.

And all our tests pass.

But what when we want test a call to an API method?

Again, the trick is in defining the required method in the class definition in the mock module. In this case we a method, named RetrieveHardwareUptime , to the HostExtensionData class. For this particular test we are not interested in the actual value that is returned, just in the type.

With the method in place, we can now write our test script. Note that we test the returned (and hard coded) value and the type.

Note that in the current Pester version it is not possible to test object methods. In other words we can not test, at this point in time, if the RetrieveHardwareUptime method is called.

Esxcli

For some properties and for some methods a call of the esxcli command is sometimes the only available or fastest method. In PowerCLI we have the Get-EsxCli cmdlet that allows us to do these calls. But how do we Pester test such calls?

Turns out we can mock the complete esxcli object.

We construct the $esxCliMock code block in such a way that it will return an object, with an Invoke() method, via the ScriptMethod type, that returns the value.

For the test script we will also have to create a definition for the Get-EsxCli cmdlet in our mock module. The purpose is to have a declaration of the required parameters for the Get-EsxCli cmdlet. Remember that we are replacing the regular PowerCLI module that contains the Get-EsxCli cmdlet, with our mock module.

And this results in

Conclusion

Writing Pester Unit tests for code that uses VMware PowerCLI and that works against a vSphere environment, is not intuitive at all at first sight.

One has to take into account the many peculiarities that exist. And more importantly, one has to reflect on how one can “mock” all the techniques you normally use in your VMware PowerCLI code.

I hope this short overview of some techniques will encourage you to start writing Pester tests, not only for DSCR, but also for your own scripts. In the end, having testing in place will help you a lot when you need to change/update your code.

I encourage you all to share your Pester tests for PowerCLI scripts. And I welcome all suggestions for additions or updates to this post.

Enjoy!

2 Comments

    Vitaliy

    Hi Luc.
    I have carefully studied these tips in the VMware community regarding the use of Start-Job d powercli.
    At the moment I have a task to deploy new VMs from Template in parallel.
    My script works now for sequential execution, however when I try to run it through Start-Job, then some tasks are completed with an error (approximately 50 out of 100) “New-VM” or with an error “New -VM Value cannot be null “. In this case, all the parameters that are used in New-VM are well collected and displayed when using Start-Job. At the same time, I also noticed that if you run tasks with sleep -secconds in about 30 milliseconds, then more tasks complete successfully.
    Could you help me in solving this problem?
    Thank you for your help and understanding, I look forward to your reply.

      LucD

      Hi Vitaliy,
      I would need to see your code to analyse what might be the problem.
      You can use the Contact form on here to send me the script as an attachment.

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.