This post is about my experience converting the CodePlex project, SQL Server Powershell Extensions (SQLPSX) Powershell V1 function libraries into PowerShell V2 Advanced functions within modules.
In order to provide context for people reading this blog post a quick timeline is needed:
The manifest files themselves are really easy to create. After you've created a module (.psm1), run new-modulemanifest and enter the information when prompted.
param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [ValidateScript({$_.GetType().Namespace -like "Microsoft.SqlServer.Management.Smo*"})] $smo, [Parameter(Position=1, Mandatory=$false)] [Microsoft.SqlServer.Management.Smo.ScriptingOptions]$scriptingOptions=$(New-SqlScriptingOptions) )
OK, so this items really isn't about Powershell V2 rather it's a change in process for me. As part of the conversion I wanted to adopt a testing framework and perform more rigorous testing. I first heard of a Powershell based xUnit testing framework on the Powerscripting podcast episode 80 in which Jon and Hal interviewed Klaus Graefensteiner about his CodePlex project PSUnit. So, I decided to try PSUnit and I've been very happy with the results. Following the directions on the PSUnit site it is a cinch to setup. PSUnit integrates with Powershell ISE. A menu item is added to execute Unit tests:
It should be noted that although I'm using PSUnit to test Powershell functions this doesn't mean that's all its good for. In fact the purpose of the PSUnt is to perform full unit testing of your .NET applications. You can test just about anything (.NET, COM, etc). For my purposes I'm interested in testing my own Powershell functions. As a script developer the easiest thing you can do with PSUnit is to create a test function for each of your functions and verify the output object is the type you expected. Here's an example test function for Get-SqlServer:
function Test.Get-SqlServer([switch] $Category_GetSql) { #Arrange #Act $Actual = Get-SqlServer "$env:computername\sql2K8" Write-Debug $Actual #Assert Assert-That -ActualValue $Actual -Constraint {$ActualValue.GetType().Name -eq 'Server'} }
Embracing the pipeline is part of writing Powershell scripts to be well, more Powershell-like. In Powershell V1 I adopted a style of writing functions created Keith Hill as described in his blog post titled "Writing CMDLETs in PowerShell". The post shows us how to write functions to accept both command argument and pipeline input. Powershell V2 makes creating a function to accept both command argument and pipeline even easier. As example let's look at a Powershell V1 function and the equivalent Powershell V2 function:
Powershell V1 function:
function Get-SqlScripter { param($smo, $scriptingOptions=$(Set-SqlScriptingOptions)) begin { function Select-SqlScripter ($smo, $scriptingOptions=$(Set-SqlScriptingOptions)) { $smo.Script($scriptingOptions) } #Select-SqlScripter } process { if ($_) { if ($_.GetType().Namespace -like "Microsoft.SqlServer.Management.Smo*") { Write-Verbose "Get-SqlScripter $($_.Name)" Select-SqlScripter $_ $scriptingOptions } else { throw 'Get-SqlScripter:Param `$smo must be an SMO object.' } } } end { if ($smo) { $smo | Get-SqlScripter -scriptingOptions $scriptingOptions } } } Powershell V2 function:
function Get-SqlScripter { param( [Parameter(Position=0, Mandatory=$true, ValueFromPipeline = $true)] [ValidateScript({$_.GetType().Namespace -like "Microsoft.SqlServer.Management.Smo*"})] $smo, [Parameter(Position=1, Mandatory=$false)] [Microsoft.SqlServer.Management.Smo.ScriptingOptions]$scriptingOptions=$(New-SqlScriptingOptions) ) process { $smo.Script($scriptingOptions) } }
new-object PSObject -property (&"$scriptRoot\replscriptopts.ps1") | add-member scriptproperty ScriptOptions ` { $scriptOptions = [Microsoft.SqlServer.Replication.ScriptOptions]::None $this | get-member -type NoteProperty | where {$this.($_.name)} | foreach {$scriptOptions = $scriptOptions -bor [Microsoft.SqlServer.Replication.ScriptOptions]::($_.name)} $scriptOptions } -passthru