TestHelper.psm1
<#
Script Constants used by *-ResourceDesigner Functions #> <# .SYNOPSIS Creates a new nuspec file for nuget package. Will create $packageName.nuspec in $destinationPath .EXAMPLE New-Nuspec -packageName "TestPackage" -version 1.0.1 -licenseUrl "http://license" -packageDescription "description of the package" -tags "tag1 tag2" -destinationPath C:\temp #> function New-Nuspec { param ( [Parameter(Mandatory=$true)] [string] $packageName, [Parameter(Mandatory=$true)] [string] $version, [Parameter(Mandatory=$true)] [string] $author, [Parameter(Mandatory=$true)] [string] $owners, [string] $licenseUrl, [string] $projectUrl, [string] $iconUrl, [string] $packageDescription, [string] $releaseNotes, [string] $tags, [Parameter(Mandatory=$true)] [string] $destinationPath ) $year = (Get-Date).Year $content += "<?xml version=""1.0""?> <package xmlns=""http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd""> <metadata> <id>$packageName</id> <version>$version</version> <authors>$author</authors> <owners>$owners</owners>" if (-not [string]::IsNullOrEmpty($licenseUrl)) { $content += " <licenseUrl>$licenseUrl</licenseUrl>" } if (-not [string]::IsNullOrEmpty($projectUrl)) { $content += " <projectUrl>$projectUrl</projectUrl>" } if (-not [string]::IsNullOrEmpty($iconUrl)) { $content += " <iconUrl>$iconUrl</iconUrl>" } $content +=" <requireLicenseAcceptance>true</requireLicenseAcceptance> <description>$packageDescription</description> <releaseNotes>$releaseNotes</releaseNotes> <copyright>Copyright $year</copyright> <tags>$tags</tags> </metadata> </package>" if (-not (Test-Path -Path $destinationPath)) { New-Item -Path $destinationPath -ItemType Directory > $null } $nuspecPath = Join-Path $destinationPath "$packageName.nuspec" New-Item -Path $nuspecPath -ItemType File -Force > $null Set-Content -Path $nuspecPath -Value $content } <# .SYNOPSIS Will attempt to download the module from PowerShellGallery using Nuget package and return the module. If already installed will return the module without making changes. If module could not be downloaded it will return null. .PARAMETER Force Used to force any installations to occur without confirming with the user. .PARAMETER moduleName Name of the module to install .PARAMETER modulePath Path where module should be installed .EXAMPLE Install-ModuleFromPowerShellGallery .EXAMPLE if ($env:APPVEYOR) { # Running in AppVeyor so force silent install of xDSCResourceDesigner $PSBoundParameters.Force = $true } $xDSCResourceDesignerModuleName = "xDscResourceDesigner" $xDSCResourceDesignerModulePath = "$env:USERPROFILE\Documents\WindowsPowerShell\Modules\$xDSCResourceDesignerModuleName" $xDSCResourceDesignerModule = Install-ModuleFromPowerShellGallery -ModuleName $xDSCResourceDesignerModuleName -ModulePath $xDSCResourceDesignerModulePath @PSBoundParameters #> function Install-ModuleFromPowerShellGallery { [OutputType([System.Management.Automation.PSModuleInfo])] [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'High')] Param ( [Parameter(Mandatory=$true)] [String] $moduleName, [Parameter(Mandatory=$true)] [String] $modulePath, [Boolean]$Force = $false ) $module = Get-Module -Name $moduleName -ListAvailable if (@($module).Count -ne 0) { # Module is already installed - report it. Write-Host -Object (` 'Version {0} of the {1} module is already installed.' ` -f $($module.Version -join ', '),$moduleName ) -ForegroundColor:Yellow # Could check for a newer version available here in future and perform an update. # Return only the latest version of the module return $module ` | Sort-Object -Property Version -Descending ` | Select-Object -First 1 } Write-Verbose -Message (` 'The {0} module is not installed.' ` -f $moduleName ) $OutputDirectory = "$(Split-Path -Path $modulePath -Parent)\" # Use Nuget directly to download the module $nugetPath = 'nuget.exe' # Can't assume nuget.exe is available - look for it in Path if ((Get-Command $nugetPath -ErrorAction SilentlyContinue) -eq $null) { # Is it in temp folder? $nugetPath = Join-Path -Path $ENV:Temp -ChildPath $nugetPath if (-not (Test-Path -Path $nugetPath)) { # Nuget.exe can't be found - download it to temp folder $NugetDownloadURL = 'http://nuget.org/nuget.exe' If ($Force -or $PSCmdlet.ShouldProcess( ` "Download Nuget.exe from '{0}' to Temp folder" ` -f $NugetDownloadURL)) { Invoke-WebRequest $NugetDownloadURL -OutFile $nugetPath Write-Verbose -Message (` "Nuget.exe was installed from '{0}' to Temp folder." ` -f $NugetDownloadURL ) } else { # Without Nuget.exe we can't continue Write-Warning -Message (` 'Nuget.exe was not installed. {0} module can not be installed automatically.' ` -f $moduleName ) return $null } } else { Write-Verbose -Message 'Using Nuget.exe found in Temp folder.' } } $nugetSource = 'https://www.powershellgallery.com/api/v2' If ($Force -or $PSCmdlet.ShouldProcess(( ` "Download and install the {0} module from '{1}' using Nuget" ` -f $moduleName,$nugetSource))) { # Use Nuget.exe to install the module $null = & "$nugetPath" @( ` 'install', $moduleName, ` '-source', $nugetSource, ` '-outputDirectory', $OutputDirectory, ` '-ExcludeVersion' ` ) $ExitCode = $LASTEXITCODE if ($ExitCode -ne 0) { throw ( 'Installation of {0} module using Nuget failed with exit code {1}.' ` -f $moduleName,$ExitCode ) } Write-Host -Object (` 'The {0} module was installed using Nuget.' ` -f $moduleName ) -ForegroundColor:Yellow } else { Write-Warning -Message (` '{0} module was not installed automatically.' ` -f $moduleName ) return $null } return (Get-Module -Name $moduleName -ListAvailable) } <# .SYNOPSIS Initializes an enviroment for running unit or integration tests on a DSC resource. This includes the following things: 1. Creates a temporary working folder. 2. Updates the $env:PSModulePath to ensure the correct module is tested. 3. Backs up any settings that need to be changed to accurately test the resource. 4. Produces a test object containing any parameters that may be used for testing as well as storing the backed up settings. The above changes are reverted by calling the Restore-TestEnvironment function. This includes deleteing the temporary working folder. .PARAMETER DSCModuleName The name of the DSC Module containing the resource that the tests will be run on. .PARAMETER DSCResourceName The full name of the DSC resource that the tests will be run on. This is usually the name of the folder containing the actual resource MOF file. .PARAMETER TestType Specifies the type of tests that are being intialized. It can be: Unit: Initialize for running Unit tests on a DSC resource. Default. Integration: Initialize for running Integration tests on a DSC resource. .OUTPUT Returns a test environment object which must be passed to the Restore-TestEnvironment function to allow it to restore the system back to the original state as well as clean up and working/temp files. .EXAMPLE $TestEnvironment = Inialize-TestEnvironment ` -DSCModuleName 'xNetworking' ` -DSCResourceName 'MSFT_xFirewall' ` -TestType Unit This command will initialize the test enviroment for Unit testing the MSFT_xFirewall DSC resource in the xNetworking DSC module. .EXAMPLE $TestEnvironment = Inialize-TestEnvironment ` -DSCModuleName 'xNetworking' ` -DSCResourceName 'MSFT_xFirewall' ` -TestType Integration This command will initialize the test enviroment for Integration testing the MSFT_xFirewall DSC resource in the xNetworking DSC module. #> function Initialize-TestEnvironment { [OutputType([PSObject])] [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String] $DSCModuleName, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [String] $DSCResourceName, [Parameter(Mandatory=$true)] [ValidateSet('Unit','Integration')] [String] $TestType ) Write-Host -Object (` 'Initializing Test Environment for {0} testing of {1} in module {2}.' ` -f $TestType,$DSCResourceName,$DSCModuleName) -ForegroundColor:Yellow if ($TestType -eq 'Unit') { [String] $RelativeModulePath = "DSCResources\$DSCResourceName\$DSCResourceName.psm1" } else { [String] $RelativeModulePath = "$DSCModuleName.psd1" } # Unique Temp Working Folder - always gets removed on completion # The tests can put anything in here and it will get cleaned up. [String] $RandomFileName = [System.IO.Path]::GetRandomFileName() [String] $WorkingFolder = Join-Path -Path $env:Temp -ChildPath "$DSCResourceName_$RandomFileName" # Create the working folder if it doesn't exist (it really shouldn't anyway) if (-not (Test-Path -Path $WorkingFolder)) { New-Item -Path $WorkingFolder -ItemType Directory } # The folder where this module is found [String] $moduleRoot = Split-Path -Parent (Split-Path -Parent (Split-Path -Parent (Split-Path -Parent $Script:MyInvocation.MyCommand.Path))) # The folder that all tests will find this module in [string] $modulesFolder = Split-Path -Parent $moduleRoot # Import the Module $Splat = @{ Path = $moduleRoot ChildPath = $RelativeModulePath Resolve = $true ErrorAction = 'Stop' } $DSCModuleFile = Get-Item -Path (Join-Path @Splat) # Remove all copies of the module from memory so an old one is not used. if (Get-Module -Name $DSCModuleFile.BaseName -All) { Get-Module -Name $DSCModuleFile.BaseName -All | Remove-Module } # Import the Module to test. Import-Module -Name $DSCModuleFile.FullName -Force # Set the PSModulePath environment variable so that the module path this module is in # appears first because the LCM will use this path to try and locate modules when integration # tests are called. This is to ensure the correct module is tested. [String] $OldModulePath = $env:PSModulePath [String] $NewModulePath = $OldModulePath if (($NewModulePath).Split(';') -ccontains $modulesFolder) { # Remove the existing module from the module path if it exists $NewModulePath = ($NewModulePath -split ';' | Where-Object {$_ -ne $modulesFolder}) -join ';' } $NewModulePath = "$modulesFolder;$NewModulePath" $env:PSModulePath = $NewModulePath [System.Environment]::SetEnvironmentVariable('PSModulePath',$NewModulePath,[System.EnvironmentVariableTarget]::Machine) # Preserve and set the execution policy so that the DSC MOF can be created $OldExecutionPolicy = Get-ExecutionPolicy if ($OldExecutionPolicy -ne 'Unrestricted') { Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Force } # Generate the test environment object that will be returned $TestEnvironment = @{ DSCModuleName = $DSCModuleName DSCResourceName = $DSCResourceName TestType = $TestType RelativeModulePath = $RelativeModulePath WorkingFolder = $WorkingFolder OldModulePath = $OldModulePath OldExecutionPolicy = $OldExecutionPolicy } return $TestEnvironment } <# .SYNOPSIS Restores the enviroment after running unit or integration tests on a DSC resource. This restores the following changes made by calling Initialize-TestEnvironemt: 1. Deletes the Working folder. 2. Restores the $env:PSModulePath if it was changed. 3. Restores any settings that were changed to test the resource. .PARAMETER TestEnvironment This is the object created by the Initialize-TestEnvironment cmdlet. .EXAMPLE Restore-TestEnvironment -TestEnvironment $TestEnvironment This command will initialize the test enviroment for Unit testing the MSFT_xFirewall DSC resource in the xNetworking DSC module. #> function Restore-TestEnvironment { [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [PSObject] $TestEnvironment ) Write-Verbose -Message (` 'Cleaning up Test Environment after {0} testing of {1} in module {2}.' ` -f $TestEnvironment.TestType,$TestEnvironment.DSCResourceName,$TestEnvironment.DSCModuleName) # Restore PSModulePath if ($TestEnvironment.OldModulePath -ne $env:PSModulePath) { $env:PSModulePath = $TestEnvironment.OldModulePath [System.Environment]::SetEnvironmentVariable('PSModulePath',$env:PSModulePath,[System.EnvironmentVariableTarget]::Machine) } # Restore the Execution Policy if ($TestEnvironment.OldExecutionPolicy -ne (Get-ExecutionPolicy)) { Set-ExecutionPolicy -ExecutionPolicy $TestEnvironment.OldExecutionPolicy -Force } # Cleanup Working Folder if (Test-Path -Path $TestEnvironment.WorkingFolder) { Remove-Item -Path $TestEnvironment.WorkingFolder -Recurse -Force } } |