I was writing some Pester unit tests recently for a PowerShell script that creates a modified version of a template file before it is then uploaded via an API and deleted. The script uses Out-File
to output the updated file and because it only exists temporarily the location it writes to is hard coded in the script.
I wanted to write some tests that would validate the file was created and with the expected updated values. I could have just let the script write the file to it's default location and validate it there, but I'd have to mock Remove-Item
to stop it deleting the file and then have my test script remove it afterwards. Another approach might have been to do some refactoring, have the output file and input parameter of the script, or break the script up into smaller functions, but I didn't particularly want to change the script.
Pester provides an automatic location TestDrive: that can be used to isolate file operations somewhere unique and that it automatically cleans up when the test completes, so all I needed to do was override the behaviour of Out-File
to write to TestDrive:
. You can do that with a Mock
, and the one-weird-trick here is to store the Out-File
command into a variable so that we can use it within the Mock
, but change it's input parameters:
BeforeAll {
$OutFile = Get-Command -Name 'Out-File'
Mock Out-File -ParameterFilter { $FilePath -match 'file.txt' } -MockWith {
$InputObject = $PesterBoundParameters['InputObject']
$FilePath = Join-Path $TestDrive 'file.txt'
& $OutFile -InputObject $InputObject -FilePath $FilePath
}
}
It 'Should update the file with the expected values' {
$FilePath = (Join-Path $TestDrive 'file.txt')
$FilePath | Should -FileContentMatch 'expectedstring1'
$FilePath | Should -FileContentMatch 'expectedstring2'
$FilePath | Should -FileContentMatch 'expectedstring3'
}
It is necessary to put the command into a variable because once a command is mocked in a test script, calling it would invoke the Mock
rather than the command, so you'd end up in a recursive loop. Note how the Mock
uses $PesterBoundParameters
to access the parameters that were passed to the command in the script, but we then override what would have been provided for -FilePath
with our TestDrive:
pathed value.
My script still mocked Remove-Item
btw, so we could check that it had been called the expected number of times. There was no need to let it actually do a file removal because the clean up of TestDrive:
takes care of that for us. It's usually best to mock away the destructive behaviour of cmdlets when testing so that if the script has been badly modified in some way you don't risk invoking the actual behaviour for the purpose of running your tests.
Top comments (0)