Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Assert assertions to Pester #2428

Merged
merged 63 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
567549c
Copy paste assert
nohwnd Apr 1, 2024
28761e6
Before adding format nicely2
nohwnd Apr 1, 2024
079d374
Few more tests
nohwnd Apr 1, 2024
cfaa965
Add code, did not fix help yet
nohwnd Apr 2, 2024
911a465
Fix $error
nohwnd Apr 6, 2024
0bf4752
Fix warnings
nohwnd Apr 6, 2024
b54b003
Fix collecting from pipeline
nohwnd Apr 6, 2024
6575ca4
Merge branch 'main' into add-assert
nohwnd Apr 7, 2024
50182d9
wip
nohwnd Apr 7, 2024
333417c
Fix input collecting
nohwnd Apr 7, 2024
0230d8c
Remove Compatibility.ps1
nohwnd Apr 7, 2024
3d1d896
Add Should-* aliases
nohwnd Apr 7, 2024
b0ffc38
Rename internal Should-* functions to Should-*Assertion to avoid conf…
nohwnd Apr 7, 2024
d60f535
Fix raw get-variable
nohwnd Apr 12, 2024
1815b55
Merge branch 'dev/6.x.x' into add-assert
nohwnd Apr 12, 2024
36f36c9
No SDK pre-release
nohwnd Apr 12, 2024
9829ae1
Fixes
nohwnd Apr 19, 2024
9143030
Add Should-BeEmptyString and fix formatting
nohwnd Apr 19, 2024
6e79b45
Build on dev/*
nohwnd Apr 20, 2024
e5a4446
Add not be null or empty, and not be null or whitespace string assert…
nohwnd Apr 20, 2024
0560d77
formatting almost fixed
nohwnd Apr 20, 2024
5b1ebd4
Change formats, fix all tests
nohwnd Apr 21, 2024
5b54c22
Update azure-pipelines.yml
nohwnd Apr 21, 2024
60c3a12
Add faster and before date time
nohwnd Apr 21, 2024
67e886d
Merge branch 'add-assert' of https://github.com/pester/Pester into ad…
nohwnd Apr 21, 2024
9e43582
Faster, slower, before, after
nohwnd Apr 23, 2024
0e5c073
before, after, faster, slower
nohwnd Apr 23, 2024
1565699
Strict mode
nohwnd Apr 23, 2024
d80c7ef
Fix running tests
nohwnd Apr 23, 2024
caaffae
all and any
nohwnd Apr 23, 2024
cc03bf4
all and any
nohwnd Apr 23, 2024
bdc7fcf
Why did pass thru failing now? :/
nohwnd Apr 23, 2024
28ae51f
Rename assertion files
nohwnd Apr 25, 2024
c70bdd2
rename some files
nohwnd Apr 25, 2024
6dd68d6
Continue on renaming and using Should-* in tests
nohwnd Apr 26, 2024
432e02d
Rename assertions and tests, and use assertion functions so we are su…
nohwnd Apr 26, 2024
b5f8da4
Fix tests
nohwnd May 1, 2024
2f262f3
Change messages,tests are broken
nohwnd May 1, 2024
13307ca
Fix messages
nohwnd May 3, 2024
ab36078
Fix more messages
nohwnd May 3, 2024
a1f283c
Little bit of help
nohwnd May 3, 2024
8509a5c
Add help
nohwnd May 7, 2024
4c8bae2
info
nohwnd May 7, 2024
a6affe3
unroll
nohwnd May 7, 2024
3c74309
Add Help
nohwnd May 14, 2024
46f46ac
Add help and put back help tests
nohwnd May 14, 2024
81ac71d
Merge branch 'main' into add-assert
nohwnd May 18, 2024
90a887d
assert
nohwnd May 18, 2024
7a971c5
Update src/functions/assert/General/Should-NotHaveType.ps1
nohwnd May 20, 2024
a18e746
Update src/functions/assert/General/Should-HaveType.ps1
nohwnd May 20, 2024
1b3f0b5
Update src/functions/assert/Equivalence/Should-BeEquivalent.ps1
nohwnd May 20, 2024
29f5cb8
Revert test, fix safe commands etc.
nohwnd May 20, 2024
7931820
Merge branch 'add-assert' of https://github.com/pester/Pester into ad…
nohwnd May 20, 2024
03e28d0
Revert launch.json
nohwnd May 20, 2024
bf19cb3
Remove TimeSpanOrStringWithTimeUnit.cs
nohwnd May 20, 2024
f1a3810
Apply suggestions from code review
nohwnd May 20, 2024
40595dc
Merge branch 'add-assert' of https://github.com/pester/Pester into ad…
nohwnd May 20, 2024
4e67f2f
Apply suggestions from code review
nohwnd May 20, 2024
0e17209
Fixes
nohwnd May 20, 2024
548e373
Merge branch 'add-assert' of https://github.com/pester/Pester into ad…
nohwnd May 20, 2024
162fab7
Get-equivalencyoption help
nohwnd May 20, 2024
6fa12ce
Get-equivalencyoption help
nohwnd May 20, 2024
c7886fb
Merge branch 'main' into add-assert
nohwnd May 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,12 @@ $script = @(
"$PSScriptRoot/src/Pester.Runtime.ps1"
"$PSScriptRoot/src/TypeClass.ps1"
"$PSScriptRoot/src/Format.ps1"
# TODO: the original version of Format2 from assert, because it does not format strings and other stuff in Pester specific way. I've used this regex (Format-Collection|Format-Object|Format-Null|Format-Boolean|Format-ScriptBlock|Format-Number|Format-Hashtable|Format-Dictionary|Format-Nicely|Get-DisplayProperty|Get-ShortType|Format-Type), '$12' to replace in VS Code.
"$PSScriptRoot/src/Format2.ps1"
"$PSScriptRoot/src/Pester.RSpec.ps1"
"$PSScriptRoot/src/Main.ps1"

"$PSScriptRoot/src/functions/assert/*/*"
"$PSScriptRoot/src/functions/assertions/*"
"$PSScriptRoot/src/functions/*"

Expand Down
106 changes: 106 additions & 0 deletions docs/assertion-types.md
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.
3 changes: 2 additions & 1 deletion global.json
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
}
}
194 changes: 194 additions & 0 deletions src/Format2.ps1
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
nohwnd marked this conversation as resolved.
Show resolved Hide resolved
}

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 #>
nohwnd marked this conversation as resolved.
Show resolved Hide resolved

# 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"
}