SlideShare ist ein Scribd-Unternehmen logo
1 von 13
Unit Testing
PowerShell
Matt Wrock (@mwrockx)
April 22 – 24
Microsoft campus
Redmond
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:devboxstartertestsInvoke-Reboot.tests.ps1
Describing 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 2ms
Tests completed in 41ms
Passed: 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.
PowerShell
Managed
• 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
Abstracting the untestableFunction Under Test
function Install-ChocolateyVsixPackage {
$installer = Join-Path $env:VS110COMNTOOLS "..IDEVsixInstaller.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 EXE
function 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 Wrapper
Context "When VSIX is already installed" {
Mock Install-Vsix {return 1001}
Install-ChocolateyVsixPackage
It "should succeed" {
Assert-MockCalled Write-ChocolateySuccess
}
}
Mocking Cmdletsfunction Get-LatestVSVersion {
$versions=(
get-ChildItem HKLM:SOFTWAREWow6432NodeMicrosoftVisualStudio `
-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:SOFTWAREWow6432NodeMicrosoftVisualStudio"
}
Mock get-itemproperty {@{InstallDir=$Path}}
$result=Get-LatestVSVersion
It "should return version 11" {
$result | Should Be 11
}
}
Isolating file operationsfunction 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 "$testRootboxstarter" | Out-Null
$Boxstarter.BaseDir="$testRootBoxstarter"
Set-BoxstarterShare
It "Should create Boxstarter Share"{
Test-Path "$env:ComputernameBoxstarter" | 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 Exceptionsfunction 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 "$testRootboxstarter" | Out-Null
$Boxstarter.BaseDir="$testRootBoxstarter"
Net share Boxstarter="$($Boxstarter.BaseDir)"
try {Set-BoxstarterShare} catch{$ex=$_}
It "Should throw exception"{
$ex | should not be $null
}
net share Boxstarter /delete
}
Debugging Tests
Doug Finke’s IsePester
https://github.com/dfinke/IsePester
Chocolatey (downloads pester,
isepester and imports them in your
ISE profile):
cinst IsePester
Ctrl+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)pesterbinpester.bat" />
</Target>
</Project>
Simple MSBuild script
A better way: Psake
https://github.com/psake/psake
$psake.use_exit_on_error = $true
properties {
$baseDir = (Split-Path -parent $psake.build_script_dir)
}
Task default -depends Test
Task Test {
pushd "$baseDir"
$pesterDir = (dir $env:ChocolateyInstalllibPester*)
if($pesterDir.length -gt 0) {$pesterDir = $pesterDir[-1]}
exec {."$pesterDirtoolsbinPester.bat" $baseDir/Tests }
popd
}
A PowerShell CI Build
Adding test detail to TeamCity builds:
https://github.com/pester/Pester/wiki/Showing-Test-Results-in-TeamCity
PowerShell Unit Test Samples
• Chocolatey
https://github.com/chocolatey/chocolatey/tree/master/tests
• Pester
https://github.com/pester/Pester/tree/master/Functions
• Boxstarter
http://boxstarter.codeplex.com/

Weitere ähnliche Inhalte

Was ist angesagt?

Store and Process Big Data with Hadoop and Cassandra
Store and Process Big Data with Hadoop and CassandraStore and Process Big Data with Hadoop and Cassandra
Store and Process Big Data with Hadoop and CassandraDeependra Ariyadewa
 
VPN Access Runbook
VPN Access RunbookVPN Access Runbook
VPN Access RunbookTaha Shakeel
 
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Arian Gutierrez
 
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebChristian Baranowski
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeStripe
 
Correcting Common Async/Await Mistakes in .NET
Correcting Common Async/Await Mistakes in .NETCorrecting Common Async/Await Mistakes in .NET
Correcting Common Async/Await Mistakes in .NETBrandon Minnick, MBA
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeMongoDB
 
Indexing & query optimization
Indexing & query optimizationIndexing & query optimization
Indexing & query optimizationJared Rosoff
 
第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」
第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」
第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」Tsuyoshi Yamamoto
 
Async all around us (promises)
Async all around us (promises)Async all around us (promises)
Async all around us (promises)Francisco Ferreira
 
Power shell examples_v4
Power shell examples_v4Power shell examples_v4
Power shell examples_v4JoeDinaso
 
The promise of asynchronous PHP
The promise of asynchronous PHPThe promise of asynchronous PHP
The promise of asynchronous PHPWim Godden
 
File System Operations
File System OperationsFile System Operations
File System OperationsG.C Reddy
 
Why Redux-Observable?
Why Redux-Observable?Why Redux-Observable?
Why Redux-Observable?Anna Su
 
TDD in the wild
TDD in the wildTDD in the wild
TDD in the wildBrainhub
 
Getting Started With MongoDB
Getting Started With MongoDBGetting Started With MongoDB
Getting Started With MongoDBBill Kunneke
 

Was ist angesagt? (20)

Store and Process Big Data with Hadoop and Cassandra
Store and Process Big Data with Hadoop and CassandraStore and Process Big Data with Hadoop and Cassandra
Store and Process Big Data with Hadoop and Cassandra
 
VPN Access Runbook
VPN Access RunbookVPN Access Runbook
VPN Access Runbook
 
Q
QQ
Q
 
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
Tipo virus espia con esto aprenderan a espiar a personas etc jeropas de mrd :v
 
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und GebBDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
BDD - Behavior Driven Development Webapps mit Groovy Spock und Geb
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
 
Correcting Common Async/Await Mistakes in .NET
Correcting Common Async/Await Mistakes in .NETCorrecting Common Async/Await Mistakes in .NET
Correcting Common Async/Await Mistakes in .NET
 
Building Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at StripeBuilding Real Time Systems on MongoDB Using the Oplog at Stripe
Building Real Time Systems on MongoDB Using the Oplog at Stripe
 
Indexing & query optimization
Indexing & query optimizationIndexing & query optimization
Indexing & query optimization
 
第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」
第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」
第3回Grails/Groovy勉強会名古屋「Grails名古屋座談会」
 
Async all around us (promises)
Async all around us (promises)Async all around us (promises)
Async all around us (promises)
 
はじめてのGroovy
はじめてのGroovyはじめてのGroovy
はじめてのGroovy
 
Power shell examples_v4
Power shell examples_v4Power shell examples_v4
Power shell examples_v4
 
The promise of asynchronous PHP
The promise of asynchronous PHPThe promise of asynchronous PHP
The promise of asynchronous PHP
 
File System Operations
File System OperationsFile System Operations
File System Operations
 
Why Redux-Observable?
Why Redux-Observable?Why Redux-Observable?
Why Redux-Observable?
 
MongoDB-SESSION03
MongoDB-SESSION03MongoDB-SESSION03
MongoDB-SESSION03
 
TDD in the wild
TDD in the wildTDD in the wild
TDD in the wild
 
ES6 generators
ES6 generatorsES6 generators
ES6 generators
 
Getting Started With MongoDB
Getting Started With MongoDBGetting Started With MongoDB
Getting Started With MongoDB
 

Andere mochten auch

それでも僕はユニットテストを書きたい - Pester powered by PowerShell
それでも僕はユニットテストを書きたい - Pester powered by PowerShellそれでも僕はユニットテストを書きたい - Pester powered by PowerShell
それでも僕はユニットテストを書きたい - Pester powered by PowerShellHidari Ikw
 
PowerShell v4 Desired State Configuration
PowerShell v4 Desired State ConfigurationPowerShell v4 Desired State Configuration
PowerShell v4 Desired State ConfigurationJason Stangroome
 
PowerShell crashcourse for Sharepoint admins
PowerShell crashcourse for Sharepoint adminsPowerShell crashcourse for Sharepoint admins
PowerShell crashcourse for Sharepoint adminsConcentrated Technology
 
Free tools for win server administration
Free tools for win server administrationFree tools for win server administration
Free tools for win server administrationConcentrated Technology
 
Automating Active Directory mgmt in PowerShell
Automating Active Directory mgmt in PowerShellAutomating Active Directory mgmt in PowerShell
Automating Active Directory mgmt in PowerShellConcentrated Technology
 
Ive got a powershell secret
Ive got a powershell secretIve got a powershell secret
Ive got a powershell secretChris Conte
 
Advanced Tools & Scripting with PowerShell 3.0 Jump Start - Certificate
Advanced Tools & Scripting with PowerShell 3.0 Jump Start - CertificateAdvanced Tools & Scripting with PowerShell 3.0 Jump Start - Certificate
Advanced Tools & Scripting with PowerShell 3.0 Jump Start - CertificateDon Reese
 

Andere mochten auch (20)

AOP on Android
AOP on AndroidAOP on Android
AOP on Android
 
それでも僕はユニットテストを書きたい - Pester powered by PowerShell
それでも僕はユニットテストを書きたい - Pester powered by PowerShellそれでも僕はユニットテストを書きたい - Pester powered by PowerShell
それでも僕はユニットテストを書きたい - Pester powered by PowerShell
 
PowerShell v4 Desired State Configuration
PowerShell v4 Desired State ConfigurationPowerShell v4 Desired State Configuration
PowerShell v4 Desired State Configuration
 
PowerShell crashcourse for Sharepoint admins
PowerShell crashcourse for Sharepoint adminsPowerShell crashcourse for Sharepoint admins
PowerShell crashcourse for Sharepoint admins
 
PowerShell custom properties
PowerShell custom propertiesPowerShell custom properties
PowerShell custom properties
 
PS scripting and modularization
PS scripting and modularizationPS scripting and modularization
PS scripting and modularization
 
Automating ad with powershell
Automating ad with powershellAutomating ad with powershell
Automating ad with powershell
 
PowerShell crashcourse
PowerShell crashcoursePowerShell crashcourse
PowerShell crashcourse
 
Server Core2
Server Core2Server Core2
Server Core2
 
Free tools for win server administration
Free tools for win server administrationFree tools for win server administration
Free tools for win server administration
 
Combining output from multiple sources
Combining output from multiple sourcesCombining output from multiple sources
Combining output from multiple sources
 
Automating Active Directory mgmt in PowerShell
Automating Active Directory mgmt in PowerShellAutomating Active Directory mgmt in PowerShell
Automating Active Directory mgmt in PowerShell
 
Ive got a powershell secret
Ive got a powershell secretIve got a powershell secret
Ive got a powershell secret
 
Implementing dr w. hyper v clustering
Implementing dr w. hyper v clusteringImplementing dr w. hyper v clustering
Implementing dr w. hyper v clustering
 
Managing SQLserver
Managing SQLserverManaging SQLserver
Managing SQLserver
 
Ha & drs gotcha's
Ha & drs gotcha'sHa & drs gotcha's
Ha & drs gotcha's
 
PowerShell crashcourse for sharepoint
PowerShell crashcourse for sharepointPowerShell crashcourse for sharepoint
PowerShell crashcourse for sharepoint
 
No-script PowerShell v2
No-script PowerShell v2No-script PowerShell v2
No-script PowerShell v2
 
Advanced Tools & Scripting with PowerShell 3.0 Jump Start - Certificate
Advanced Tools & Scripting with PowerShell 3.0 Jump Start - CertificateAdvanced Tools & Scripting with PowerShell 3.0 Jump Start - Certificate
Advanced Tools & Scripting with PowerShell 3.0 Jump Start - Certificate
 
PowerShell 8tips
PowerShell 8tipsPowerShell 8tips
PowerShell 8tips
 

Ähnlich wie Unit testing powershell

Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShellGaetano Causio
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsBastian Feder
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownpartsBastian Feder
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitsmueller_sandsmedia
 
Real world cross-platform testing
Real world cross-platform testingReal world cross-platform testing
Real world cross-platform testingPeter Edwards
 
The Future of JVM Languages
The Future of JVM Languages The Future of JVM Languages
The Future of JVM Languages VictorSzoltysek
 
node.js Module Development
node.js Module Developmentnode.js Module Development
node.js Module DevelopmentJay Harris
 
Javascript Continues Integration in Jenkins with AngularJS
Javascript Continues Integration in Jenkins with AngularJSJavascript Continues Integration in Jenkins with AngularJS
Javascript Continues Integration in Jenkins with AngularJSLadislav Prskavec
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownpartsBastian Feder
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosDivante
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB jhchabran
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 
Power shell voor developers
Power shell voor developersPower shell voor developers
Power shell voor developersDennis Vroegop
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11Michelangelo van Dam
 
solving little problems
solving little problemssolving little problems
solving little problemsAustin Ziegler
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowKacper Gunia
 
SharePoint Administration with PowerShell
SharePoint Administration with PowerShellSharePoint Administration with PowerShell
SharePoint Administration with PowerShellEric Kraus
 
Painless Persistence with Realm
Painless Persistence with RealmPainless Persistence with Realm
Painless Persistence with RealmChristian Melchior
 

Ähnlich wie Unit testing powershell (20)

Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShell
 
PhpUnit - The most unknown Parts
PhpUnit - The most unknown PartsPhpUnit - The most unknown Parts
PhpUnit - The most unknown Parts
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnitinternational PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
international PHP2011_Bastian Feder_The most unknown Parts of PHPUnit
 
Real world cross-platform testing
Real world cross-platform testingReal world cross-platform testing
Real world cross-platform testing
 
The Future of JVM Languages
The Future of JVM Languages The Future of JVM Languages
The Future of JVM Languages
 
node.js Module Development
node.js Module Developmentnode.js Module Development
node.js Module Development
 
Javascript Continues Integration in Jenkins with AngularJS
Javascript Continues Integration in Jenkins with AngularJSJavascript Continues Integration in Jenkins with AngularJS
Javascript Continues Integration in Jenkins with AngularJS
 
Php unit the-mostunknownparts
Php unit the-mostunknownpartsPhp unit the-mostunknownparts
Php unit the-mostunknownparts
 
Why is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenariosWhy is crud a bad idea - focus on real scenarios
Why is crud a bad idea - focus on real scenarios
 
Agile Swift
Agile SwiftAgile Swift
Agile Swift
 
Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB Introduction à CoffeeScript pour ParisRB
Introduction à CoffeeScript pour ParisRB
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 
groovy & grails - lecture 12
groovy & grails - lecture 12groovy & grails - lecture 12
groovy & grails - lecture 12
 
Power shell voor developers
Power shell voor developersPower shell voor developers
Power shell voor developers
 
Unit testing with zend framework tek11
Unit testing with zend framework tek11Unit testing with zend framework tek11
Unit testing with zend framework tek11
 
solving little problems
solving little problemssolving little problems
solving little problems
 
Forget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers CracowForget about Index.php and build you applications around HTTP - PHPers Cracow
Forget about Index.php and build you applications around HTTP - PHPers Cracow
 
SharePoint Administration with PowerShell
SharePoint Administration with PowerShellSharePoint Administration with PowerShell
SharePoint Administration with PowerShell
 
Painless Persistence with Realm
Painless Persistence with RealmPainless Persistence with Realm
Painless Persistence with Realm
 

Unit testing powershell

  • 1. Unit Testing PowerShell Matt Wrock (@mwrockx) April 22 – 24 Microsoft campus Redmond
  • 2. 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:devboxstartertestsInvoke-Reboot.tests.ps1 Describing 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 2ms Tests completed in 41ms Passed: 6 Failed: 0
  • 3. 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?
  • 4. Managed Unit Testing Tools vs. PowerShell Managed • 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
  • 6. Abstracting the untestableFunction Under Test function Install-ChocolateyVsixPackage { $installer = Join-Path $env:VS110COMNTOOLS "..IDEVsixInstaller.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 EXE function 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 Wrapper Context "When VSIX is already installed" { Mock Install-Vsix {return 1001} Install-ChocolateyVsixPackage It "should succeed" { Assert-MockCalled Write-ChocolateySuccess } }
  • 7. Mocking Cmdletsfunction Get-LatestVSVersion { $versions=( get-ChildItem HKLM:SOFTWAREWow6432NodeMicrosoftVisualStudio ` -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:SOFTWAREWow6432NodeMicrosoftVisualStudio" } Mock get-itemproperty {@{InstallDir=$Path}} $result=Get-LatestVSVersion It "should return version 11" { $result | Should Be 11 } }
  • 8. Isolating file operationsfunction 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 "$testRootboxstarter" | Out-Null $Boxstarter.BaseDir="$testRootBoxstarter" Set-BoxstarterShare It "Should create Boxstarter Share"{ Test-Path "$env:ComputernameBoxstarter" | 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:
  • 9. Testing Exceptionsfunction 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 "$testRootboxstarter" | Out-Null $Boxstarter.BaseDir="$testRootBoxstarter" Net share Boxstarter="$($Boxstarter.BaseDir)" try {Set-BoxstarterShare} catch{$ex=$_} It "Should throw exception"{ $ex | should not be $null } net share Boxstarter /delete }
  • 10. Debugging Tests Doug Finke’s IsePester https://github.com/dfinke/IsePester Chocolatey (downloads pester, isepester and imports them in your ISE profile): cinst IsePester Ctrl+F5 Debugs tests in the active editor
  • 11. 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)pesterbinpester.bat" /> </Target> </Project> Simple MSBuild script A better way: Psake https://github.com/psake/psake $psake.use_exit_on_error = $true properties { $baseDir = (Split-Path -parent $psake.build_script_dir) } Task default -depends Test Task Test { pushd "$baseDir" $pesterDir = (dir $env:ChocolateyInstalllibPester*) if($pesterDir.length -gt 0) {$pesterDir = $pesterDir[-1]} exec {."$pesterDirtoolsbinPester.bat" $baseDir/Tests } popd }
  • 12. A PowerShell CI Build Adding test detail to TeamCity builds: https://github.com/pester/Pester/wiki/Showing-Test-Results-in-TeamCity
  • 13. PowerShell Unit Test Samples • Chocolatey https://github.com/chocolatey/chocolatey/tree/master/tests • Pester https://github.com/pester/Pester/tree/master/Functions • Boxstarter http://boxstarter.codeplex.com/