unit testing powershell

Post on 15-Jan-2015

1.705 Views

Category:

Documents

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

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/

top related