PowerShell: how to unit test your cmdlet

If you miss VS, intellisense, TD.NET, etc., you might want to try extending PowerShell with custom cmdlets, which are .NET classes deriving from Cmdlet. They allow you to extend PowerShell while still programming in your favorite language.

I'm also hooked to Test Driven Design (that's what TDD should mean, IMO), so I naturally looked for a way to develop my cmdlets in a TDD way. Turns out that it's fairly easy. For this example, I will show a simple cmd-let that should load an assembly in a flexible way (by file name, full name or partial name).

Note that there's no way to call ProcessRecord directly. The way to run the cmdlet is to call Invoke, and getting an enumerator from it. Remember that when placed in the pipeline, the cmdlet will be called once for each input in the pipeline. Let's now implement the cmdlet to make the test pass (and compile!):

Note that in order to return output from your cmdlet, you call WriteObject. It's interesting to debug the test we wrote. You will notice that the call to Invoke doesn't actually execute the cmdlet. Instead, moving the enumerator does. This is how the pipeline achieves lazy evaluation of each "step" and continues executing with the following commands. Very cool.

And that's pretty much all there is to it. Now you can start adding tests and the corresponding features to your cmdlet, with the amazing piece of mind that comes from having a unit test that says that it actually works ;). Needless to say, this test-code-run is much faster than testing the cmdlet directly in PowerShell (you need to constantly exit PS and re-enter, re-add your snapin, etc., otherwise the output assembly gets locked).

3 Comments

This is a good approach, Daniel.

Two other techniques that we use on the PowerShell team are:

1) Hosting powershell (via our hosting / Runspace APIs.) In this, you create a pipeline, and execute your cmdlet that way. This lets you test cmdlets that derive from PsCmdlet, and still allows you to use the unit testing harness of choice.

2) Writing scripts that test your cmdlet. This lets you test your cmdlet in the same environment that your users will run them from, and helps uncover some subtle usability issues and bugs. It requires that you write your own unit testing harness, though. I'll be blogging one soon, but it's not very difficult :)