However, obstacles may arise when you want to write unit tests for PowerShell modules. In this post I’m going to present a solution that enables you to overcome some of these obstacles.
Let’s get started.
Let’s take the code above as an example.
It’s a PowerShell module that exposes just one function, Invoke-NUnit, but it relies heavily on two other functions; Get-NUnitPackage and Invoke-Executable.
Let’s now say that we want to write a test for the Invoke-NUnit function. One of them could look like this:
We need to mock the calls to those “helper” functions.
Firstly because we don’t actually want to execute any executable and secondly because the Get-NUnitPackage function currently throws an exception (it’s not finished yet).
Lastly, mocking allows us to write expectations that test whether the code actually does what we expect it to do (like using the labels switch as above).
That is a unit test, tested in isolation.
Now, we have some problems though, the module only exposes the “public” function.
- We could expose the other functions as well, maybe with a conditional export in ‘test-only’ context.
The real problem will however still reside with modules and the scope rules that surround them.
- We could break out the separate functions into their own .ps1 files and “dot source” them into the module. This would probably work…but.
What if I don’t want to do that…we just want that one file!
All I really want to do is dot source the .psm1 file just as I would have done with any other .ps1 file. The problem is that dot sourcing doesn’t work with any other file types than .ps1
At the top of the test file we’ll put this snippet of code:
$module = (Split-Path -Leaf $PSCommandPath).Replace(".Tests.ps1", ".psm1") $code = Get-Content $module | Out-String Invoke-Expression $code
If the test file follows Pester naming conventions and therefore is named Invoke-NUnit.Tests.ps1 we can simply replace part of the name to get to the module/psm1 file (line 1 shows that).
Secondly we’ll get the contents of the .psm1 file (Get-Content) and put it into a string (line 2).
Lastly, we’ll use Invoke-Expression to just run that code just as if we would have dot sourced the file (line 3).
And now it works just as if we would have used a .ps1 file although we didn’t…
Yes, there’s one at least and that is you can have trouble debugging since you’re no longer using the “real” module but just the contents of it so to say. I haven’t found a way to debug code that has been put into a script using Invoke-Expression at least.
However, if you use TDD practices you normally won’t need to do much debugging and setting breakpoints…my experience at least. Just trust the expectations.
note: the code used in this post can be found right here.