Last active
April 14, 2021 17:21
-
-
Save sean-m/fe79fceb337946f56627def4551cd1b6 to your computer and use it in GitHub Desktop.
Don't use this! Use Pester. Simple functions for writing assertion tests that will throw exceptions in a composable way without any external dependencies. All test functions return $true if passed so they can be used as a predicate, they throw on failure to stop script execution. This exists to simplify testing for invariants in production scrip…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class AssertionException : System.Exception { | |
AssertionException ([string]$Message, [System.Exception]$InnerException) : base($Message,$InnerException) { } | |
} | |
function Assert-Contains { | |
param ( | |
[object[]]$Collection, | |
[string]$Property=$null, | |
$Pattern, | |
[Parameter(Mandatory=$true)] | |
[string]$ErrorMsg | |
) | |
if ($Property) { | |
if (-not ($Collection.$Property -like $Pattern)) { | |
throw New-Object System.Exception ($ErrorMsg) | |
} | |
} | |
else { | |
if (-not ($Collection -like $Pattern)) { | |
throw New-Object System.Exception ($ErrorMsg) | |
} | |
} | |
$true | |
} | |
function Assert-Comparison { | |
param ( | |
[object[]]$Collection, | |
[string]$Property=$null, | |
[ValidateSet('lt','le','eq','ge','gt')] | |
$Operator, | |
$Value, | |
[Parameter(Mandatory=$true)] | |
[string]$ErrorMsg | |
) | |
$result = $false | |
foreach ($item in $Collection) { | |
$lvalue = $item | |
if ($Property) { | |
$lvalue = $item.$Property | |
} | |
switch ($Operator) { | |
'lt' { $result = $lvalue -lt $Value } | |
'le' { $result = $lvalue -le $Value } | |
'eq' { $result = $lvalue -eq $Value } | |
'ge' { $result = $lvalue -ge $Value } | |
'gt' { $result = $lvalue -gt $Value } | |
} | |
if ($result) { break } | |
} | |
if (-not $result) { | |
throw New-Object System.Exception ($ErrorMsg) | |
} | |
return $true | |
} | |
function Assert-Exists { | |
param ( | |
$FilePath, | |
[ValidateSet("Any","Container","Leaf","Folder","File")] | |
$Type="Any" | |
) | |
$_type = $null | |
switch ($Type) { | |
'File' { | |
$_type = 'Leaf' | |
} | |
'Folder' { | |
$_type = 'Container' | |
} | |
default { | |
$_type = $Type | |
} | |
} | |
if (-not (Test-Path -Path $FilePath -PathType $_type)) { | |
throw New-Object System.Exception ("Path does not exist: '$FilePath' with type: $Type") | |
} | |
$true | |
} | |
function Assert-All { | |
# Of dubious utility | |
# Could just call assertions in-line | |
param ( | |
[object[]]$Assertions | |
) | |
$count=1 | |
try { | |
foreach ($a in $Assertions) { | |
& $a | Out-Null | |
$count++ | |
} | |
} | |
catch { | |
$assertion_string = $Assertions[$count-1].ToString() | |
$ex = [AssertionException]::new("Assertion $count failed! : $assertion_string", $_.Exception) | |
throw $ex | |
} | |
$true | |
} | |
function Get-ExceptionMessages { | |
param ( | |
[Exception]$Exception, | |
[string]$Message="", | |
[int]$Level=1 | |
) | |
if ($Exception.Message) { | |
if ($Message) { | |
$Message += "`n$Level :`t$($Exception.Message)" | |
} | |
else { | |
$Message = "Exception messages, outer to inner:`n$Level :`t$($Exception.Message)" | |
} | |
} | |
$result=$null | |
if ($Exception.InnerException) { | |
$result = Get-ExceptionMessages -Exception $Exception.InnerException -Message $Message -Level ($Level+1) | |
} | |
else { | |
$result = $Message | |
} | |
return $result | |
} | |
## Test for the existence of files or anything with a PSProvider: registry, ActiveDirectory, etc. | |
Assert-Exists -FilePath C:\Intel | |
Assert-Exists -FilePath C:\Intel -Type File | |
Assert-Exists -FilePath C:\Intel -Type Folder | |
## Test that a comparison is true for at least one item in a collection | |
# Works with testing DateTime values | |
Assert-Comparison -Collection @([DateTime]::MaxValue, (Get-Date)) -Operator lt -Value (Get-Date) -ErrorMsg "Nothing in the past" | |
# Can compare property values | |
$files = Get-ChildItem . | |
Assert-Comparison -Collection $files -Property 'LastWriteTime' -Operator ge -Value ((Get-Date).AddDays(-1)) -ErrorMsg "Does not contain recent files." | |
## Test that at a simple pattern matches at least one item in a collection | |
# Works with values or properties | |
class Person { | |
$Name | |
$Age | |
} | |
$1 = New-Object Person -Property @{ Name="Patrick"; Age=10 } | |
$2 = New-Object Person -Property @{ Name="Alex"; Age=8 } | |
$3 = New-Object Person -Property @{ Name="Nicole"; Age=6 } | |
Assert-Contains -Collection @('Apple','Orange','Banana','Corn') -Pattern "*orn" -ErrorMsg "Whoops" | |
Assert-Contains -Collection @($1, $2, $3) -Property 'Name' -Pattern "Nic*" -ErrorMsg "Whoops" | |
Assert-Contains -Collection @($1, $2, $3) -Property 'Age' -Pattern 6 -ErrorMsg "Whoops" | |
## Verify that a collection of assertions are true and throw once if they arent. | |
try { | |
Assert-All -Assertions @( | |
{ Assert-Exists -FilePath C:\Intel }, | |
{ Assert-Contains -Collection @('Apple','Orange','Banana','Corn') -Pattern "*orn" -ErrorMsg "Whoops" }, | |
{ Assert-Contains -Collection @('Apple','Orange','Banana') -Pattern "*orn" -ErrorMsg "Whoops" }, | |
{ Assert-Contains -Collection @($1, $2, $3) -Property 'Name' -Pattern "Pat*" -ErrorMsg "Whoops" } | |
) | |
} | |
catch { | |
# Recurse into inner exceptions and stack messages for convenience with logging. | |
Get-ExceptionMessages -Exception $_.Exception | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment