diff --git a/docs/commands/Should-HaveParameter.mdx b/docs/commands/Should-HaveParameter.mdx index 2462260..16f614b 100644 --- a/docs/commands/Should-HaveParameter.mdx +++ b/docs/commands/Should-HaveParameter.mdx @@ -37,11 +37,49 @@ This assertion inspects command metadata and can also verify parameter details s ### EXAMPLE 1 ```powershell -Get-Command "Invoke-WebRequest" | Should -HaveParameter Uri -Mandatory +Get-Command Invoke-WebRequest | Should-HaveParameter Uri -Type ([uri]) -Mandatory ``` -This test passes, because it expected the parameter URI to exist and to -be mandatory. +This assertion passes, because `Invoke-WebRequest` has a mandatory `-Uri` parameter of type `[uri]`. + +### EXAMPLE 2 + +```powershell +function Get-Cat { + [CmdletBinding(DefaultParameterSetName = 'ByName')] + param( + [Parameter(ParameterSetName = 'ByName', Mandatory)] + [Alias('Id')] + [string] $Name, + + [Parameter(ParameterSetName = 'ByIndex', Mandatory)] + [int] $Index, + + [ValidateSet('Json', 'Xml')] + [string] $Format = 'Json' + ) +} + +Describe 'Get-Cat public contract' { + It 'requires a Name' { + Get-Command Get-Cat | Should-HaveParameter Name -Type ([string]) -Mandatory -Alias 'Id' + } + + It 'defaults Format to Json' { + Get-Command Get-Cat | Should-HaveParameter Format -Type ([string]) -DefaultValue 'Json' + } +} +``` + +A typical real-life use is locking down the public API of your own command. These assertions pass, because `-Name` is a mandatory `[string]` with the alias `Id`, and `-Format` is an optional `[string]` that defaults to `Json`. + +### EXAMPLE 3 + +```powershell +Get-Command Get-Cat | Should-HaveParameter Index -InParameterSet 'ByIndex' +``` + +This assertion passes, because the `-Index` parameter (from the `Get-Cat` function above) belongs to the `ByIndex` parameter set. ## PARAMETERS diff --git a/docs/commands/Should-Invoke.mdx b/docs/commands/Should-Invoke.mdx index 3c0dc92..6a9c1dc 100644 --- a/docs/commands/Should-Invoke.mdx +++ b/docs/commands/Should-Invoke.mdx @@ -55,100 +55,94 @@ passed to Should-Invoke, Should-Invoke will throw an exception. ### EXAMPLE 1 ```powershell -Mock Set-Content {} +function Save-Report ($Path, $Content) { + Set-Content -Path $Path -Value $Content +} + +Describe 'Save-Report' { + It 'writes the report to disk' { + Mock Set-Content -{... -Some Code ...} + Save-Report -Path 'report.txt' -Content 'All systems green' -Should-Invoke Set-Content + Should-Invoke Set-Content -Times 1 -Exactly + } +} ``` -This will throw an exception and cause the test to fail if Set-Content is not called in Some Code. +Asserts that `Save-Report` wrote to disk by calling the mocked `Set-Content` exactly once. The test fails if `Set-Content` was not called, or was called more than once. ### EXAMPLE 2 ```powershell -Mock Set-Content -parameterFilter {$path.StartsWith("$env:temp\")} +Mock Set-Content -{... -Some Code ...} +Save-Report -Path 'report.txt' -Content 'All systems green' -Should-Invoke Set-Content 2 { $path -eq "$env:temp\test.txt" } +Should-Invoke Set-Content -ParameterFilter { $Path -eq 'report.txt' } ``` -This will throw an exception if some code calls Set-Content on $path=$env:temp\test.txt less than 2 times +Only the calls where `-Path` was `report.txt` are counted. The assertion passes, because `Save-Report` wrote to that path. ### EXAMPLE 3 ```powershell -Mock Set-Content {} +function Get-Weather ($City) { + Invoke-RestMethod -Uri "https://api.example.com/weather?city=$City" +} -{... -Some Code ...} +Mock Invoke-RestMethod -Should-Invoke Set-Content 0 +Get-Weather -City 'Oslo' + +Should-Invoke Invoke-RestMethod -Times 1 -Exactly -ParameterFilter { $Uri -match 'city=Oslo' } ``` -This will throw an exception if some code calls Set-Content at all +Asserts that the weather API was queried exactly once, and that the request was made for the city of Oslo. ### EXAMPLE 4 ```powershell -Mock Set-Content {} -``` +Describe 'Save-Report' { + BeforeAll { Mock Set-Content } -\{... -Some Code ...\} + It 'writes exactly once per call' { + Save-Report -Path 'a.txt' -Content 'x' -Should-Invoke Set-Content -Exactly 2 + Should-Invoke Set-Content -Times 1 -Exactly -Scope It + } +} +``` -This will throw an exception if some code does not call Set-Content Exactly two times. +`-Scope It` counts only the calls made in the current `It` block, even though the mock is shared by the whole `Describe`. ### EXAMPLE 5 ```powershell -Describe 'Should-Invoke Scope behavior' { - Mock Set-Content { } +Describe 'Publish-Thing' { + It 'writes from inside the module' { + Mock -ModuleName Toolbox Set-Content - It 'Calls Set-Content at least once in the It block' { - {... -Some Code ...} + Publish-Thing - Should-Invoke Set-Content -Exactly 0 -Scope It + Should-Invoke -ModuleName Toolbox Set-Content -Times 1 -Exactly } } ``` -Checks for calls only within the current It block. +When the command under test lives in a module, both `Mock` and `Should-Invoke` must use the same `-ModuleName` so the recorded call is found. ### EXAMPLE 6 ```powershell -Describe 'Describe' { - Mock -ModuleName SomeModule Set-Content { } - -{... -Some Code ...} - - It 'Calls Set-Content at least once in the Describe block' { - Should-Invoke -ModuleName SomeModule Set-Content - } -} -``` +Mock Remove-Item -Checks for calls to the mock within the SomeModule module. -Note that both the Mock -and Should-Invoke commands use the same module name. +Remove-TempFile -Path "$env:TEMP/old.log" -### EXAMPLE 7 - -```powershell -Should-Invoke Get-ChildItem -ExclusiveFilter { $Path -eq 'C:\' } +Should-Invoke Remove-Item -ExclusiveFilter { $Path -like "$env:TEMP*" } ``` -Checks to make sure that Get-ChildItem was called at least one time with -the -Path parameter set to 'C:\', and that it was not called at all with -the -Path parameter set to any other value. +`-ExclusiveFilter` passes only if *every* recorded call matches the filter. Here it asserts that `Remove-Item` was called at least once, and only ever for paths inside the temp folder. It is a shorthand for pairing a `Should-Invoke` and a `Should-NotInvoke`. ## PARAMETERS diff --git a/docs/commands/Should-MatchString.mdx b/docs/commands/Should-MatchString.mdx index bcd0fa8..487a92d 100644 --- a/docs/commands/Should-MatchString.mdx +++ b/docs/commands/Should-MatchString.mdx @@ -37,18 +37,26 @@ The `-match` operator is case-insensitive by default, but you can make it case-s ### EXAMPLE 1 ```powershell -"hello" | Should-MatchString "h.*o" +(New-Guid).Guid | Should-MatchString '^[0-9a-f-]{36}$' ``` -This assertion will pass, because the actual value matches the regular expression pattern. +This assertion passes, because a GUID is made up of 36 lowercase hexadecimal and dash characters. ### EXAMPLE 2 ```powershell -"hello" | Should-MatchString "H.*O" -CaseSensitive +'user-4f2a' | Should-MatchString '^user-[0-9a-f]{4}$' ``` -This assertion will fail, because the actual value does not case-sensitively match the regular expression pattern. +This assertion passes, because the generated id matches the expected `user-` prefix followed by four hexadecimal characters. This is handy for checking that a function returns ids, tokens or file names in the format you expect. + +### EXAMPLE 3 + +```powershell +'Pester 6.0.0' | Should-MatchString 'pester \d+\.\d+\.\d+' -CaseSensitive +``` + +This assertion fails, because with `-CaseSensitive` the lowercase `pester` in the pattern does not match the capitalized `Pester` in the actual value. ## PARAMETERS diff --git a/docs/commands/Should-NotHaveParameter.mdx b/docs/commands/Should-NotHaveParameter.mdx index 17855a6..d487e23 100644 --- a/docs/commands/Should-NotHaveParameter.mdx +++ b/docs/commands/Should-NotHaveParameter.mdx @@ -37,10 +37,22 @@ It only checks the parameter name, unlike `Should-HaveParameter`, which can also ### EXAMPLE 1 ```powershell -Get-Command "Invoke-WebRequest" | Should -NotHaveParameter Uri +Get-Command Get-Date | Should-NotHaveParameter Uri ``` -This test fails, because it expected the parameter URI to not exist. +This assertion passes, because `Get-Date` has no `-Uri` parameter. + +### EXAMPLE 2 + +```powershell +function Get-PublicReport { + param([string] $Name) +} + +Get-Command Get-PublicReport | Should-NotHaveParameter Credential +``` + +This assertion passes, because `Get-PublicReport` does not expose a `-Credential` parameter. This is useful for guarding against accidentally adding parameters you want to keep off a command's public surface. ## PARAMETERS @@ -106,11 +118,6 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## NOTES -The attribute [ArgumentCompleter] was added with PSv5. -Previously this -assertion will not be able to use the -HasArgumentCompleter parameter -if the attribute does not exist. - ## RELATED LINKS [https://pester.dev/docs/commands/Should-NotHaveParameter](https://pester.dev/docs/commands/Should-NotHaveParameter) diff --git a/docs/commands/Should-NotInvoke.mdx b/docs/commands/Should-NotInvoke.mdx index 475cdab..d1e8f59 100644 --- a/docs/commands/Should-NotInvoke.mdx +++ b/docs/commands/Should-NotInvoke.mdx @@ -54,100 +54,50 @@ passed to Should-NotInvoke, Should-NotInvoke will throw an exception. ### EXAMPLE 1 ```powershell -Mock Set-Content {} -``` - -\{... -Some Code ...\} +function Remove-TempFile ($Path, [switch] $WhatIf) { + if (-not $WhatIf) { Remove-Item -Path $Path } +} -Should-NotInvoke Set-Content +Describe 'Remove-TempFile' { + It 'does not delete anything in -WhatIf mode' { + Mock Remove-Item -This will throw an exception and cause the test to fail if Set-Content is not called in Some Code. - -### EXAMPLE 2 + Remove-TempFile -Path 'temp.txt' -WhatIf -```powershell -Mock Set-Content -parameterFilter {$path.StartsWith("$env:temp\")} + Should-NotInvoke Remove-Item + } +} ``` -\{... -Some Code ...\} - -Should-NotInvoke Set-Content 2 \{ $path -eq "$env:temp\test.txt" \} - -This will throw an exception if some code calls Set-Content on $path=$env:temp\test.txt less than 2 times - -### EXAMPLE 3 - -```powershell -Mock Set-Content {} -``` - -\{... -Some Code ...\} - -Should-NotInvoke Set-Content 0 - -This will throw an exception if some code calls Set-Content at all +Because `-WhatIf` was passed, `Remove-TempFile` must not delete anything. The assertion passes when the mocked `Remove-Item` was never called, and throws (failing the test) if it was called. -### EXAMPLE 4 +### EXAMPLE 2 ```powershell -Mock Set-Content {} -``` - -\{... -Some Code ...\} - -Should-NotInvoke Set-Content -Exactly 2 +Mock Remove-Item -This will throw an exception if some code does not call Set-Content Exactly two times. +Remove-TempFile -Path "$env:TEMP/old.log" -### EXAMPLE 5 - -```powershell -Describe 'Should-NotInvoke Scope behavior' { - Mock Set-Content { } +Should-NotInvoke Remove-Item -ParameterFilter { $Path -notlike "$env:TEMP*" } ``` - It 'Calls Set-Content at least once in the It block' \{ - \{... -Some Code ...\} - - Should-NotInvoke Set-Content -Exactly 0 -Scope It - \} -\} +Only the calls whose `-Path` is outside the temp folder are counted. The assertion passes, because `Remove-TempFile` only ever deletes inside `$env:TEMP`. -Checks for calls only within the current It block. - -### EXAMPLE 6 +### EXAMPLE 3 ```powershell -Describe 'Describe' { - Mock -ModuleName SomeModule Set-Content { } -``` - -\{... -Some Code ...\} +Describe 'Remove-TempFile' { + BeforeAll { Mock Remove-Item } - It 'Calls Set-Content at least once in the Describe block' \{ - Should-NotInvoke -ModuleName SomeModule Set-Content - \} -\} + It 'is a no-op in -WhatIf mode' { + Remove-TempFile -Path 'temp.txt' -WhatIf -Checks for calls to the mock within the SomeModule module. -Note that both the Mock -and Should-NotInvoke commands use the same module name. - -### EXAMPLE 7 - -```powershell -Should-NotInvoke Get-ChildItem -ExclusiveFilter { $Path -eq 'C:\' } + Should-NotInvoke Remove-Item -Scope It + } +} ``` -Checks to make sure that Get-ChildItem was called at least one time with -the -Path parameter set to 'C:\', and that it was not called at all with -the -Path parameter set to any other value. +`-Scope It` limits the check to calls made in the current `It` block, even when the mock is shared across the whole `Describe`. ## PARAMETERS diff --git a/docs/commands/Should-NotMatchString.mdx b/docs/commands/Should-NotMatchString.mdx index 158fd6c..18b8d4e 100644 --- a/docs/commands/Should-NotMatchString.mdx +++ b/docs/commands/Should-NotMatchString.mdx @@ -37,18 +37,26 @@ The `-notmatch` operator is case-insensitive by default, but you can make it cas ### EXAMPLE 1 ```powershell -"hello" | Should-NotMatchString "^world$" +'Hello Jakub, your order #4821 shipped.' | Should-NotMatchString '\{\{.*?\}\}' ``` -This assertion will pass, because the actual value does not match the regular expression pattern. +This assertion passes, because the rendered text contains no leftover `{{ ... }}` template placeholders. This is a common check after expanding a template to make sure every token was replaced. ### EXAMPLE 2 ```powershell -"hello" | Should-NotMatchString "h.*o" -CaseSensitive +'level=info msg="started"' | Should-NotMatchString 'password=' ``` -This assertion will fail, because the actual value case-sensitively matches the regular expression pattern. +This assertion passes, because the log line does not leak a `password=` value. + +### EXAMPLE 3 + +```powershell +'Build failed' | Should-NotMatchString 'failed' -CaseSensitive +``` + +This assertion fails, because the actual value case-sensitively contains `failed`. ## PARAMETERS