Lær, hvordan du skriver tests mod PowerShell Universal med Pester 5.
Installer forudsætninger
I dette indlæg bruger vi InvokeBuild og Pester. Du skal installere disse moduler for at følge med.
Install-Module Pester
Install-Module InvokeBuild
Download PowerShell Universal
Første skridt er at downloade og installere den version af PowerShell Universal, du vil teste din konfiguration imod. Det kan du gøre på to måder. Du kan bruge Invoke-WebRequest direkte til at downloade ZIP-filen, eller du kan bruge Install-PSUServer til at downloade MSI-filen og installere tjenesten.
I dette eksempel bruger jeg Invoke-WebRequest, fordi jeg downloader nightly-buildet. Scriptet herunder læser nightly-bloblageret, finder det seneste build, downloader ZIP-filen, pakker den ud og fjerner blokeringen af filerne.
[xml]$Xml = (Invoke-RestMethod 'https://imsreleases.blob.core.windows.net/universal-nightly?restype=container&comp=list').Substring(3)
$MaxBlob = $null
foreach($blob in $Xml.EnumerationResults.Blobs.Blob.Where({$_.Url.Contains('win7') -and $_.Url.Contains(".zip")}))
{
if ($null -eq $MaxBlob -or ([int]$blob.Name.Split('/')[0]) -gt ([int]$MaxBlob.Name.Split('/')[0]))
{
$MaxBlob = $blob
}
}
Invoke-WebRequest $MaxBlob.Url -OutFile "$PSScriptRoot\Universal.zip"
Expand-Archive -Path "$PSScriptRoot\Universal.zip" -Destination "$PSScriptRoot\Universal"
Get-ChildItem "$PSScriptRoot\Universal" -Recurse | Unblock-File
Du kan også tilpasse scriptet ovenfor, så det downloader en udgivet version.
Invoke-WebRequest "https://imsreleases.blob.core.windows.net/universal/production/2.5.4/Universal.win7-x64.2.5.4.zip" -OutFile "$PSScriptRoot\Universal.zip"
Konfigurer PowerShell Universal
Når PowerShell Universal er downloadet, kan vi konfigurere PSU med nogle forhåndsbyggede konfigurationsfiler. I vores integrationstestsuite har vi .ps1-filer, som vi indlæser i konfigurationsmappen, før serveren startes.
Vores testfiler har mange varianter af PSU-konfiguration. Her er et udsnit af vores endpoints.ps1.
New-PSUEndpoint -Url "/get" -Method 'GET' -Endpoint {
"Hello"
}
New-PSUEndpoint -Url "/get/header" -Method 'GET' -Endpoint {
$Headers["X-MYHEADER"]
}
New-PSUEndpoint -Url "/get/:id" -Method 'GET' -Endpoint {
$Id
}
New-PSUEndpoint -Url "/post" -Method 'POST' -Endpoint {
$Body
}
New-PSUEndpoint -Url "/post/params" -Method 'POST' -Endpoint {
param($Name, $Value)
@{
Name = $Name
Value = $Value
}
}
New-PSUEndpoint -Url "/error" -Endpoint {
throw "Uh oh!"
} -ErrorAction stop
Derefter tager vi denne konfiguration og udruller den til konfigurationsmappen C:\ProgramData\UniversalAutomation\Repository.
New-Item C:\ProgramData\UniversalAutomation -ItemType Directory
Copy-Item "$PSScriptRoot\assets\Repository" C:\ProgramData\UniversalAutomation -Recurse
Klargør en Pester-test
Dernæst kan vi få PSU-serveren op at køre og klar til vores Pester-testsuite. Scriptet herunder starter PSU-serveren, venter på, at den bliver aktiv, logger ind og udsteder derefter et app-token, som vi kan bruge senere i testene.
Import-Module "$PSScriptRoot\Universal\Universal.psd1"
$Process = Start-Process "$PSScriptRoot\Universal\Universal.Server.exe" -PassThru
# Wait for the PSU server to start
while($true)
{
try
{
Invoke-RestMethod "http://localhost:5000/api/v1/alive"
break;
}
catch
{
}
}
try
{
# Sign in using default forms auth.
Invoke-RestMethod "http://localhost:5000/api/v1/signin" -Method Post -Body (@{
Username = "admin"
Password = "admin"
} | ConvertTo-Json) -SessionVariable Session -ContentType "application/json"
# Grant an app token using the login session
$ENV:TestAppToken = (Invoke-RestMethod "http://localhost:5000/api/v1/apptoken/grant" -Method GET -WebSession $Session).Token
# Connect to the PSU server so we can use cmdlets
Connect-PSUServer -AppToken $ENV:TestAppToken -ComputerName "http://localhost:5000"
# Set the current location and start executing tests
Set-Location $PSScriptRoot
$Results = Invoke-Pester -PassThru
if ($Results.Result -ne 'Passed')
{
throw "Tests failed!"
}
}
finally
{
Stop-Process $Process
}
Skriv en Pester-test
Næste skridt er at køre valideringsscripts med Pester. Dette indlæg er skrevet mod Pester 5.3.1.
Her er et udsnit af nogle af de tests, vi kører for scripts. Vi udnytter en datadrevet test, der lader os køre mod flere miljøer ved at give et -ForEach-array til Describe-blokken.
Alle vores tests i dette eksempel kører tre gange, én gang for hvert miljø. Vi bruger også BeforeAll-blokken til at sætte miljøet, før testene for hvert miljø kører.
Til sidst validerer hver It-blok en enkelt funktion med cmdlets som Invoke-PSUScript.
Describe "Scripts.<_>" -ForEach @('pwsh', 'powershell', 'integrated') {
BeforeAll {
Set-PSUSetting -DefaultEnvironment $_
}
Context "Run" {
It "should have correct variables" {
$Vars = Invoke-PSUScript -Name 'Vars.ps1' -Wait
$Vars.Environment | Should -be $_
$Vars.Script.Name | Should -be "Vars.ps1"
$Vars.Job | Should -not -be $null
$Vars.JobId | Should -not -be $null
$Vars.ScriptId | should -not -be $null
$Vars.Simple | Should -be "123"
}
It "should run a script by name" {
$Job = Invoke-PSUScript -Name 'Script.ps1'
$Job | Should -not -be $null
}
It "should run a script by pipeline" {
{ Get-PSUScript -Name 'Script.ps1' | Invoke-PSUScript } | Should -Throw
}
It "should return a hashtable" {
$Output = Invoke-PSUScript -Name 'Output.ps1' -Wait
$Output.Name | Should -be 'Tutorial'
$Output.Description | Should -be 'Tutorial'
}
It "should pass parameters to script" {
$Output = Invoke-PSUScript -Name 'Params.ps1' -Wait -One 1 -Two 2
$Output.One | Should -be 1
$Output.Two | Should -be 2
}
It "should call script in folder" {
Invoke-PSUScript -Name 'Script2.ps1' -Wait | Should -be 'Hello'
}
}
}
Pak det ind i et InvokeBuild-script
Nu hvor vi har testrammen og testene, kan vi pakke dem ind i et InvokeBuild-script. Det gør det nemmere at kalde enkelte dele af workflowet.
Her er det fulde eksempel. Vi har sat tre build-opgaver op. Én rydder op efter tidligere testkørsler. Den anden downloader og pakker nightly-buildet ud. Den sidste opgave kører testsuiten.
task Clean {
Remove-Item -Path "C:\ProgramData\PowerShellUniversal" -Force -ErrorAction SilentlyContinue -Recurse
Remove-Item -Path "C:\ProgramData\UniversalAutomation" -Force -ErrorAction SilentlyContinue -Recurse
Remove-Item -Path "$PSScriptRoot\Universal" -Force -ErrorAction SilentlyContinue -Recurse
Remove-Item -Path "$PSScriptRoot\Universal.zip" -Force -ErrorAction SilentlyContinue
}
task DownloadNightly {
[xml]$Xml = (Invoke-RestMethod 'https://imsreleases.blob.core.windows.net/universal-nightly?restype=container&comp=list').Substring(3)
$MaxBlob = $null
foreach($blob in $Xml.EnumerationResults.Blobs.Blob.Where({$_.Url.Contains('win7') -and $_.Url.Contains(".zip")}))
{
if ($null -eq $MaxBlob -or ([int]$blob.Name.Split('/')[0]) -gt ([int]$MaxBlob.Name.Split('/')[0]))
{
$MaxBlob = $blob
}
}
Invoke-WebRequest $MaxBlob.Url -OutFile "$PSScriptRoot\Universal.zip"
Expand-Archive -Path "$PSScriptRoot\Universal.zip" -Destination "$PSScriptRoot\Universal"
Get-ChildItem "$PSScriptRoot\Universal" -Recurse | Unblock-File
}
task RunTests {
New-Item C:\ProgramData\UniversalAutomation -ItemType Directory
Copy-Item "$PSScriptRoot\assets\Repository" C:\ProgramData\UniversalAutomation -Recurse
Import-Module "$PSScriptRoot\Universal\Universal.psd1"
$Process = Start-Process "$PSScriptRoot\Universal\Universal.Server.exe" -PassThru
while($true)
{
try
{
Invoke-RestMethod "http://localhost:5000/api/v1/alive"
break;
}
catch
{
}
}
try
{
Invoke-RestMethod "http://localhost:5000/api/v1/signin" -Method Post -Body (@{
Username = "admin"
Password = "admin"
} | ConvertTo-Json) -SessionVariable Session -ContentType "application/json"
$ENV:TestAppToken = (Invoke-RestMethod "http://localhost:5000/api/v1/apptoken/grant" -Method GET -WebSession $Session).Token
Connect-PSUServer -AppToken $ENV:TestAppToken -ComputerName "http://localhost:5000"
Set-Location $PSScriptRoot
$Results = Invoke-Pester -PassThru
if ($Results.Result -ne 'Passed')
{
throw "Tests failed!"
}
}
finally
{
Stop-Process $Process
}
}
task CleanAndRun Clean, RunTests
task . Clean, DownloadNightly, RunTests
Kør Pester-tests i GitHub Actions
I vores miljø bruger vi GitHub Actions til CI- og CD-pipelines. Vi har en self-hosted agent, der er afsat til integrationstests. Fra Universal-repositoriet har vi sat en GitHub Actions-workflow-YAML-fil op, så testene kører på vores egen agent. Workflowet kører hver morgen og kan udløses manuelt.
Vi kalder Invoke-Build for at køre integrationstestene. Hvis en test fejler, kastes en undtagelse, og workflowet fejler også.
name: Integration Tests
on:
schedule:
- cron: "0 4 * * *"
workflow_dispatch:
jobs:
build:
name: Build
runs-on: self-hosted
steps:
- uses: actions/checkout@v1
- name: Run Integration Test
run: Invoke-Build -File .\test\integration-test.ps1
shell: pwsh
Action-outputtet viser, hvilke tests der lykkedes, og hvilke der fejlede. Her er resultatet af en af vores testsuites.
Klar til at bygge? Download PowerShell Universal.

Adam Driscoll