-
-
Notifications
You must be signed in to change notification settings - Fork 477
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Assert assertions to Pester (#2428)
Add assertions from Assert and improve them.
- Loading branch information
Showing
110 changed files
with
7,266 additions
and
83 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# Assert assertions | ||
|
||
Pester 6 preview comes with a new set of Should-* assertions. These new assertions are split these categories based on their usage: | ||
|
||
- value | ||
- generic | ||
- type specific | ||
|
||
- collection | ||
- generic | ||
- combinator | ||
|
||
Each of these categories treats `$Actual` and `$Expected` values differently, to provide a consistent behavior when using the `|` syntax. | ||
|
||
## Value vs. Collection assertions | ||
|
||
The `$Actual` value can be provided by two syntaxes, either by pipeline (`|`) or by parameter (`-Actual`): | ||
|
||
```powershell | ||
1 | Should-Be -Expected 1 | ||
Should-Be -Actual 1 -Expected 1 | ||
``` | ||
|
||
### Using pipeline syntax | ||
|
||
When using the pipeline syntax, PowerShell unwraps the input and we lose the type of the collection on the left side. We are provided with a collection that can be either $null, empty or have items. Notably, we cannot distinguish between a single value being provided, and an array of single item: | ||
|
||
```powershell | ||
1 | Should-Be | ||
@(1) | Should-Be | ||
``` | ||
|
||
These will both be received by the assertion as `@(1)`. | ||
|
||
For this reason a value assertion will handle this as `1`, but a collection assertion will handle this input as `@(1)`. | ||
|
||
Another special case is `@()`. A value assertion will handle it as `$null`, but a collection assertion will handle it as `@()`. | ||
|
||
`$null` remains `$null` in both cases. | ||
|
||
```powershell | ||
# Should-Be is a value assertion: | ||
1 | Should-Be -Expected 1 | ||
@(1) | Should-Be -Expected 1 | ||
$null | Should-Be -Expected $null | ||
@() | Should-Be -Expected $null #< --- TODO: this is not the case right now, we special case this as empty array, but is that correct? it does not play well with the value and collection assertion, and we special case it just because we can. | ||
# $null | will give $local:input -> $null , and @() | will give $local:input -> @(), is that distinction important when we know that we will only check against values? | ||
# This fails, because -Expected does not allow collections. | ||
@() | Should-Be -Expected @() | ||
```powershell | ||
# Should-BeCollection is a collection assertion: | ||
1 | Should-BeCollection -Expected @(1) | ||
@(1) | Should-BeCollection -Expected @(1) | ||
@() | Should-BeCollection -Expected @() | ||
# This fails, because -Expected requires a collection. | ||
$null | Should-BeCollection -Expected $null | ||
``` | ||
|
||
### Using the -Actual syntax | ||
|
||
The value provides to `-Actual`, is always exactly the same as provided. | ||
|
||
```powershell | ||
Should-Be -Actual 1 -Expected 1 | ||
# This fails, Actual is collection, while expected is int. | ||
Should-Be -Actual @(1) -Expected 1 | ||
``` | ||
|
||
## Value assertions | ||
|
||
### Generic value assertions | ||
|
||
Generic value assertions, such as `Should-Be`, are for asserting on a single value. They behave quite similar to PowerShell operators, e.g. `Should-Be` maps to `-eq`. | ||
|
||
The `$Expected` accepts any input that is not a collection. | ||
The type of `$Expected` determines the type to be used for the comparison. | ||
`$Actual` is automatically converted to that type. | ||
|
||
```powershell | ||
1 | Should-Be -Expected $true | ||
Get-Process -Name Idle | Should-Be -Expected "System.Diagnostics.Process (Idle)" | ||
``` | ||
|
||
The assertions in the above examples will both pass: | ||
- `1` converts to `bool` `$true`, which is the expected value. | ||
- `Get-Process` retrieves the Idle process (on Windows). This process object gets converted to `string`. The string is equal to the expected value. | ||
|
||
### Type specific value assertions | ||
|
||
Type specific assertions are for asserting on a single value of a given type. For example boolean. These assertions take the advantage of being more specialized, to provide a type specific functionality. Such as `Should-BeString -IgnoreWhitespace`. | ||
|
||
The `$Expected` accepts input that has the same type as the assertion type. E.g. `Should-BeString -Expected "my string"`. | ||
|
||
`$Actual` accepts input that has the same type as the assertion type. The input is not automatically converted to the destination type, unless the assertion specifies it, e.g. `Should-BeFalsy` will convert to `bool`. | ||
|
||
## Collection assertions | ||
|
||
|
||
|
||
These assertions are exported from the module as Assert-* functions and aliased to Should-*, this is because of PowerShell restricting multi word functions to a list of predefined approved verbs. |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
{ | ||
"sdk": { | ||
"rollForward": "latestFeature", | ||
"version": "8.0.100" | ||
"version": "8.0.100", | ||
"allowPrerelease": false | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,194 @@ | ||
function Format-Collection2 ($Value, [switch]$Pretty) { | ||
$length = 0 | ||
$o = foreach ($v in $Value) { | ||
$formatted = Format-Nicely2 -Value $v -Pretty:$Pretty | ||
$length += $formatted.Length + 1 # 1 is for the separator | ||
$formatted | ||
} | ||
|
||
$prettyLimit = 50 | ||
if ($Pretty -and ($length + 3) -gt $prettyLimit) { | ||
# 3 is for the '@()' | ||
"@(`n $($o -join ",`n ")`n)" | ||
} | ||
else { | ||
"@($($o -join ', '))" | ||
} | ||
} | ||
|
||
function Format-Object2 ($Value, $Property, [switch]$Pretty) { | ||
if ($null -eq $Property) { | ||
$Property = foreach ($p in $Value.PSObject.Properties) { $p.Name } | ||
} | ||
$orderedProperty = foreach ($p in $Property | & $SafeCommands['Sort-Object']) { | ||
# force the values to be strings for powershell v2 | ||
"$p" | ||
} | ||
|
||
$valueType = Get-ShortType $Value | ||
$items = foreach ($p in $orderedProperty) { | ||
$v = ([PSObject]$Value.$p) | ||
$f = Format-Nicely2 -Value $v -Pretty:$Pretty | ||
"$p=$f" | ||
} | ||
|
||
if (0 -eq $Property.Length ) { | ||
$o = "$valueType{}" | ||
} | ||
elseif ($Pretty) { | ||
$o = "$valueType{`n $($items -join ";`n ");`n}" | ||
} | ||
else { | ||
$o = "$valueType{$($items -join '; ')}" | ||
} | ||
|
||
$o | ||
} | ||
|
||
function Format-String2 ($Value) { | ||
if ('' -eq $Value) { | ||
return '<empty>' | ||
} | ||
|
||
"'$Value'" | ||
} | ||
|
||
function Format-Null2 { | ||
'$null' | ||
} | ||
|
||
function Format-Boolean2 ($Value) { | ||
'$' + $Value.ToString().ToLower() | ||
} | ||
|
||
function Format-ScriptBlock2 ($Value) { | ||
'{' + $Value + '}' | ||
} | ||
|
||
function Format-Number2 ($Value) { | ||
[string]$Value | ||
} | ||
|
||
function Format-Hashtable2 ($Value) { | ||
$head = '@{' | ||
$tail = '}' | ||
|
||
$entries = foreach ($v in $Value.Keys | & $SafeCommands['Sort-Object']) { | ||
$formattedValue = Format-Nicely2 $Value.$v | ||
"$v=$formattedValue" | ||
} | ||
|
||
$head + ( $entries -join '; ') + $tail | ||
} | ||
|
||
function Format-Dictionary2 ($Value) { | ||
$head = 'Dictionary{' | ||
$tail = '}' | ||
|
||
$entries = foreach ($v in $Value.Keys | & $SafeCommands['Sort-Object'] ) { | ||
$formattedValue = Format-Nicely2 $Value.$v | ||
"$v=$formattedValue" | ||
} | ||
|
||
$head + ( $entries -join '; ') + $tail | ||
} | ||
|
||
function Format-Nicely2 ($Value, [switch]$Pretty) { | ||
if ($null -eq $Value) { | ||
return Format-Null2 -Value $Value | ||
} | ||
|
||
if ($Value -is [bool]) { | ||
return Format-Boolean2 -Value $Value | ||
} | ||
|
||
if ($Value -is [string]) { | ||
return Format-String2 -Value $Value | ||
} | ||
|
||
if ($value -is [type]) { | ||
return Format-Type2 -Value $Value | ||
} | ||
|
||
if (Is-DecimalNumber -Value $Value) { | ||
return Format-Number2 -Value $Value | ||
} | ||
|
||
if (Is-ScriptBlock -Value $Value) { | ||
return Format-ScriptBlock2 -Value $Value | ||
} | ||
|
||
if (Is-Value -Value $Value) { | ||
return $Value | ||
} | ||
|
||
if (Is-Hashtable -Value $Value) { | ||
return Format-Hashtable2 -Value $Value | ||
} | ||
|
||
if (Is-Dictionary -Value $Value) { | ||
return Format-Dictionary2 -Value $Value | ||
} | ||
|
||
if ((Is-DataTable -Value $Value) -or (Is-DataRow -Value $Value)) { | ||
return Format-DataTable2 -Value $Value -Pretty:$Pretty | ||
} | ||
|
||
if (Is-Collection -Value $Value) { | ||
return Format-Collection2 -Value $Value -Pretty:$Pretty | ||
} | ||
|
||
Format-Object2 -Value $Value -Property (Get-DisplayProperty2 $Value.GetType()) -Pretty:$Pretty | ||
} | ||
|
||
function Get-DisplayProperty2 ([Type]$Type) { | ||
# rename to Get-DisplayProperty? | ||
|
||
<# some objects are simply too big to show all of their properties, | ||
so we can create a list of properties to show from an object | ||
maybe the default info from Get-FormatData could be utilized here somehow | ||
so we show only stuff that would normally show in format-table view | ||
leveraging the work PS team already did #> | ||
|
||
# this will become more advanced, basically something along the lines of: | ||
# foreach type, try constructing the type, and if it exists then check if the | ||
# incoming type is assignable to the current type, if so then return the properties, | ||
# this way I can specify the map from the most concrete type to the least concrete type | ||
# and for types that do not exist | ||
|
||
$propertyMap = @{ | ||
'System.Diagnostics.Process' = 'Id', 'Name' | ||
} | ||
|
||
$propertyMap[$Type.FullName] | ||
} | ||
|
||
function Get-ShortType2 ($Value) { | ||
if ($null -ne $value) { | ||
Format-Type2 $Value.GetType() | ||
} | ||
else { | ||
Format-Type2 $null | ||
} | ||
} | ||
|
||
function Format-Type2 ([Type]$Value) { | ||
if ($null -eq $Value) { | ||
return '[null]' | ||
} | ||
|
||
$type = [string]$Value | ||
|
||
$typeFormatted = $type ` | ||
-replace "^System\." ` | ||
-replace "^Management\.Automation\.PSCustomObject$", "PSObject" ` | ||
-replace "^PSCustomObject$", "PSObject" ` | ||
-replace "^Object\[\]$", "collection" ` | ||
|
||
"[$($typeFormatted)]" | ||
} | ||
|
||
function Format-DataTable2 ($Value) { | ||
return "$Value" | ||
} | ||
|
Oops, something went wrong.