Upload
matt-wrock
View
1.705
Download
1
Tags:
Embed Size (px)
DESCRIPTION
Citation preview
Unit Testing PowerShellMatt Wrock (@mwrockx)
April 22 – 24Microsoft campusRedmond
Describe "Invoke-Reboot" { Context "When reboots are suppressed" { Mock New-Item -parameterFilter {$Path -like "*\Boxstarter*"} Mock Restart $Boxstarter.RebootOk=$false $Boxstarter.IsRebooting=$false Invoke-Reboot
it "will not create Restart file" { Assert-MockCalled New-Item -times 0 } it "will not restart" { Assert-MockCalled Restart -times 0 } it "will not toggle reboot" { $Boxstarter.IsRebooting | should be $false } } }
A PowerShell Unit Test in the wild
I’m all about test coverage
Executing all tests in C:\dev\boxstarter\tests\Invoke-Reboot.tests.ps1Describing Invoke-Reboot When reboots are suppressed[+] will not create Restart file 6ms[+] will not restart 11ms[+] will not toggle reboot 4ms When reboots are not suppressed[+] will create Restart file 11ms[+] will restart 5ms[+] will toggle reboot 2msTests completed in 41msPassed: 6 Failed: 0
Why Test PowerShell?
Do NOT UnitTest your scripts if you like surprises!
• Your scripts will likely be easier to understand
• Catch regressions• How does your API really
feel?
Managed Unit Testing Tools vs. PowerShellManaged• Xunit - https://xunit.codeplex.com/
• Nunit - http://www.nunit.org/
• MSTest
PowerShell• Pester - https://github.com/pester/Pester
• PSUnit - http://psunit.org/
• PSTest - https://github.com/knutkj/pstest/wiki
If practical, test powershell code in PowerShell:
• Saves the overhead of starting a runspace for each test• Environment more closely resembles the user’s
PowerShell Unit Testing Patterns
Do not use these patterns in PowerShell. They are much too
twirly.
Abstracting the untestableFunction Under Test function Install-ChocolateyVsixPackage { $installer = Join-Path $env:VS110COMNTOOLS "..\IDE\VsixInstaller.exe" $download="MyVSIX.vsix" Write-Debug "Installing VSIX using $installer" $exitCode = Install-Vsix "$installer" "$download" if($exitCode -gt 0 -and $exitCode -ne 1001) { #1001: Already installed Write-ChocolateyFailure "There was an error installing." return } Write-ChocolateySuccess} Wrap the EXEfunction Install-Vsix($installer, $installFile) { Write-Host "Installing $installFile using $installer" $psi = New-Object System.Diagnostics.ProcessStartInfo $psi.FileName=$installer $psi.Arguments="/q $installFile" $s = [System.Diagnostics.Process]::Start($psi) $s.WaitForExit() return $s.ExitCode}
Test and Mock the WrapperContext "When VSIX is already installed" { Mock Install-Vsix {return 1001}
Install-ChocolateyVsixPackage
It "should succeed" { Assert-MockCalled Write-ChocolateySuccess } }
Mocking Cmdlets function Get-LatestVSVersion { $versions=( get-ChildItem HKLM:SOFTWARE\Wow6432Node\Microsoft\VisualStudio ` -ErrorAction SilentlyContinue | ? { ($_.PSChildName -match "^[0-9\.]+$") } | ? { $_.property -contains "InstallDir" } | sort {[int]($_.PSChildName)} -descending ) if($versions -and $versions.Length){ $version = $versions[0] }elseif($versions){ $version = $versions } return $version}
Context "When version 9, 10 and 11 is installed" { Mock Get-ChildItem {@( @{PSChildName="9.0";Property=@("InstallDir");PSPath="9"}, @{PSChildName="10.0";Property=@("InstallDir");PSPath="10"}, @{PSChildName="11.0";Property=@("InstallDir");PSPath="11"} )} ` -parameterFilter { $path -eq "HKLM:SOFTWARE\Wow6432Node\Microsoft\VisualStudio" } Mock get-itemproperty {@{InstallDir=$Path}}
$result=Get-LatestVSVersion It "should return version 11" { $result | Should Be 11 } }
Isolating file operations function Set-BoxstarterShare { param( [string]$shareName="Boxstarter", [string[]]$accounts=@("Everyone") )
foreach($account in $accounts){ $acctOption += "/GRANT:'$account,READ' " } IEX "net share $shareName='$($Boxstarter.BaseDir)' $acctOption" if($LastExitCode -ne 0) { Throw "Share was not succesfull." }}
Describe "Set-BoxstarterShare" { $testRoot=(Get-PSDrive TestDrive).Root
Context "When setting share with no parameters" { MkDir "$testRoot\boxstarter" | Out-Null $Boxstarter.BaseDir="$testRoot\Boxstarter"
Set-BoxstarterShare
It "Should create Boxstarter Share"{ Test-Path "\\$env:Computername\Boxstarter" | should be $true } It "Should give read access to everyone"{ (net share Boxstarter) | ? { $_.StartsWith("Permission")} | % { $_.ToLower().EndsWith("everyone, read") | Should be $true } } net share Boxstarter /delete } }
Using the Pester TestDrive:\
Testing Exceptions function Set-BoxstarterShare { param( [string]$shareName="Boxstarter", [string[]]$accounts=@("Everyone") )
foreach($account in $accounts){ $acctOption += "/GRANT:'$account,READ' " } IEX "net share $shareName='$($Boxstarter.BaseDir)' $acctOption" if($LastExitCode -ne 0) { Throw "Share was not succesfull." }}
Context "When share already exists" { MkDir "$testRoot\boxstarter" | Out-Null $Boxstarter.BaseDir="$testRoot\Boxstarter" Net share Boxstarter="$($Boxstarter.BaseDir)" try {Set-BoxstarterShare} catch{$ex=$_}
It "Should throw exception"{ $ex | should not be $null } net share Boxstarter /delete }
Debugging TestsDoug Finke’s IsePesterhttps://github.com/dfinke/IsePester
Chocolatey (downloads pester, isepester and imports them in your ISE profile):
cinst IsePesterCtrl+F5 Debugs tests in the active editor
A PowerShell CI Build<Project ToolsVersion="4.0“ DefaultTargets="Go“ xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <GoDependsOn>Tests</GoDependsOn> <Configuration>Release</Configuration> <Platform>Any CPU</Platform> </PropertyGroup>
<Target Name="Go" DependsOnTargets="$(GoDependsOn)" />
<Target Name="Tests"> <Exec Command="cmd /c $(MSBuildProjectDirectory)\pester\bin\pester.bat" /> </Target> </Project>
Simple MSBuild script
A better way: Psakehttps://github.com/psake/psake $psake.use_exit_on_error = $trueproperties { $baseDir = (Split-Path -parent $psake.build_script_dir)}
Task default -depends Test
Task Test { pushd "$baseDir" $pesterDir = (dir $env:ChocolateyInstall\lib\Pester*) if($pesterDir.length -gt 0) {$pesterDir = $pesterDir[-1]} exec {."$pesterDir\tools\bin\Pester.bat" $baseDir/Tests } popd}
A PowerShell CI BuildAdding test detail to TeamCity builds: https://github.com/pester/Pester/wiki/Showing-Test-Results-in-TeamCity
PowerShell Unit Test Samples
• Chocolateyhttps://github.com/chocolatey/chocolatey/tree/master/tests• Pester
https://github.com/pester/Pester/tree/master/Functions• Boxstarter
http://boxstarter.codeplex.com/