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

Update InvokeVerifiable error message and add negate support #1976

Merged
merged 6 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ Describe 'Remove-Cache' {

Remove-Cache

Assert-MockCalled -CommandName Remove-Item -Times 1 -Exactly
Should -Invoke -CommandName Remove-Item -Times 1 -Exactly
}
}
```
Expand Down
27 changes: 16 additions & 11 deletions src/functions/Mock.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ function New-MockBehavior {
IsDefault = $null -eq $ParameterFilter
IsInModule = -not [string]::IsNullOrEmpty($ContextInfo.TargetModule)
Verifiable = $Verifiable
Executed = $false
ScriptBlock = $MockWith
Hook = $Hook
PSTypeName = 'MockBehavior'
Expand Down Expand Up @@ -300,23 +301,27 @@ function Should-InvokeVerifiableInternal {
[CmdletBinding()]
param(
[Parameter(Mandatory)]
$Behaviors
$Behaviors,
[switch]$Negate
)

$unverified = [System.Collections.Generic.List[Object]]@()
$filteredBehaviors = [System.Collections.Generic.List[Object]]@()
foreach ($b in $Behaviors) {
if ($b.Verifiable) {
$unverified.Add($b)
if ($b.Executed -eq $Negate.IsPresent) {
$filteredBehaviors.Add($b)
}
}

if ($unVerified.Count -gt 0) {
foreach ($b in $unVerified) {
$message = "$([System.Environment]::NewLine) Expected $($b.CommandName) "
if ($filteredBehaviors.Count -gt 0) {
if ($Negate) { $message = "$([System.Environment]::NewLine)Expected no verifiable mocks to be called, but these were:" }
else { $message = "$([System.Environment]::NewLine)Expected all verifiable mocks to be called, but these were not:" }

foreach ($b in $filteredBehaviors) {
$message += "$([System.Environment]::NewLine) Command $($b.CommandName) "
if ($b.ModuleName) {
$message += "in module $($b.ModuleName) "
$message += "from inside module $($b.ModuleName) "
}
$message += "to be called with $(if ($null -ne $b.Filter) { $b.Filter.ToString().Trim() })"
if ($null -ne $b.Filter) { $message += "with { $($b.Filter.ToString().Trim()) }" }
}

return [PSCustomObject] @{
Expand Down Expand Up @@ -395,7 +400,7 @@ function Should-InvokeInternal {
ArgumentList = $historyEntry.Args
Metadata = $ContextInfo.Hook.Metadata
# do not use the callser session state from the hook, the parameter filter
# on Should -Invoke can come from a different session state if inModuleScope is used to
# on Should -Invoke can come from a different session state if inModuleScope is used to
# wrap it. Use the caller session state to which the scriptblock is bound
SessionState = $SessionState
}
Expand Down Expand Up @@ -971,7 +976,7 @@ function ExecuteBehavior {
Write-PesterDebugMessage -Scope Mock "Executing mock behavior for mock$(if ($ModuleName) {" $ModuleName -" }) $CommandName."
}

$Behavior.Verifiable = $false
$Behavior.Executed = $true

$scriptBlock = {
param (
Expand Down
6 changes: 3 additions & 3 deletions src/functions/Pester.SessionState.Mock.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The mock of the first filter to pass will be used. The exception to this
rule are Mocks with no filters. They will always be evaluated last since
they will act as a "catch all" mock.

Mocks can be marked Verifiable. If so, the Assert-VerifiableMock command
Mocks can be marked Verifiable. If so, the Should -InvokeVerifiable command
can be used to check if all Verifiable mocks were actually called. If any
verifiable mock is not called, Should -InvokeVerifiable will throw an
exception and indicate all mocks not called.
Expand Down Expand Up @@ -633,7 +633,7 @@ https://pester.dev/docs/commands/Assert-VerifiableMock
Set-ScriptBlockScope -ScriptBlock $sb -SessionState $PSCmdlet.SessionState
& $sb
}
function Should-InvokeVerifiable {
function Should-InvokeVerifiable ([switch]$Negate) {
<#
.SYNOPSIS
Checks if any Verifiable Mock has not been invoked. If so, this will throw an exception.
Expand Down Expand Up @@ -666,7 +666,7 @@ This will not throw an exception because the mock was invoked.

#>
$behaviors = @(Get-VerifiableBehaviors)
Should-InvokeVerifiableInternal -Behaviors $behaviors
Should-InvokeVerifiableInternal -Behaviors $behaviors -Negate:$Negate
}

& $script:SafeCommands['Add-ShouldOperator'] -Name InvokeVerifiable `
Expand Down
83 changes: 80 additions & 3 deletions tst/functions/Mock.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ Describe "When Applying multiple Mocks on a single command where one has no filt
}
}

Describe "When Creating a Verifiable Mock that is not called" {
Describe "When Creating Verifiable Mock that is not called" {
Context "In the test script's scope" {
It "Should throw" {
Mock FunctionUnderTest { return "I am a verifiable test" } -Verifiable -parameterFilter { $param1 -eq "one" }
Expand All @@ -642,7 +642,7 @@ Describe "When Creating a Verifiable Mock that is not called" {
$result = $_
}

$result.Exception.Message | Should -Be "$([System.Environment]::NewLine) Expected FunctionUnderTest to be called with `$param1 -eq `"one`""
$result.Exception.Message | Should -Be "$([System.Environment]::NewLine)Expected all verifiable mocks to be called, but these were not:$([System.Environment]::NewLine) Command FunctionUnderTest with { `$param1 -eq `"one`" }"
}
}

Expand All @@ -666,7 +666,7 @@ Describe "When Creating a Verifiable Mock that is not called" {
}

It "Should throw" {
$result.Exception.Message | Should -Be "$([System.Environment]::NewLine) Expected ModuleFunctionUnderTest in module TestModule to be called with `$param1 -eq `"one`""
$result.Exception.Message | Should -Be "$([System.Environment]::NewLine)Expected all verifiable mocks to be called, but these were not:$([System.Environment]::NewLine) Command ModuleFunctionUnderTest from inside module TestModule with { `$param1 -eq `"one`" }"
}

AfterAll {
Expand All @@ -675,6 +675,24 @@ Describe "When Creating a Verifiable Mock that is not called" {
}
}

Describe "When Creating multiple Verifiable Mocks that are not called" {
It "Should throw and list all commands" {
Mock FunctionUnderTest { return "I am a verifiable test" } -Verifiable -ParameterFilter { $param1 -eq "one" }
Mock FunctionUnderTest { return "I am another verifiable test" } -Verifiable -ParameterFilter { $param1 -eq "two" }
Mock FunctionUnderTest { return "I am probably called" } -Verifiable -ParameterFilter { $param1 -eq "three" }
FunctionUnderTest "three" | Out-Null
$result = $null
try {
Should -InvokeVerifiable
}
catch {
$result = $_
}

$result.Exception.Message | Should -Be "$([System.Environment]::NewLine)Expected all verifiable mocks to be called, but these were not:$([System.Environment]::NewLine) Command FunctionUnderTest with { `$param1 -eq `"one`" }$([System.Environment]::NewLine) Command FunctionUnderTest with { `$param1 -eq `"two`" }"
}
}

Describe "When Creating a Verifiable Mock that is called" {
BeforeAll {
Mock FunctionUnderTest -Verifiable -parameterFilter { $param1 -eq "one" }
Expand All @@ -686,6 +704,65 @@ Describe "When Creating a Verifiable Mock that is called" {
}
}

Describe "When calling Should -Not -InvokeVerifiable" {
Context 'All Verifiable Mocks are called' {
BeforeAll {
Mock FunctionUnderTest -Verifiable -parameterFilter { $param1 -eq "one" }
FunctionUnderTest "one"

try {
Should -Not -InvokeVerifiable
}
Catch {
$result = $_
}
}

It "Should throw" {
$result.Exception.Message | Should -Be "$([System.Environment]::NewLine)Expected no verifiable mocks to be called, but these were:$([System.Environment]::NewLine) Command FunctionUnderTest with { `$param1 -eq `"one`" }"
}
}

Context 'Some Verifiable Mocks are called' {
BeforeAll {
Mock FunctionUnderTest -Verifiable -parameterFilter { $param1 -eq "one" }
Mock FunctionUnderTest -Verifiable
FunctionUnderTest "one"

try {
Should -Not -InvokeVerifiable
}
Catch {
$result = $_
}
}

It "Should throw" {
$result.Exception.Message | Should -Be "$([System.Environment]::NewLine)Expected no verifiable mocks to be called, but these were:$([System.Environment]::NewLine) Command FunctionUnderTest with { `$param1 -eq `"one`" }"
}
}

Context 'No Verifiable Mocks exists' {
BeforeAll {
Mock FunctionUnderTest -Verifiable -parameterFilter { $param1 -eq "one" }
}

It "Should not throw" {
{ Should -Not -InvokeVerifiable } | Should -Not -Throw
}
}

Context 'None of the Verifiable Mocks is called' {
BeforeAll {
Mock FunctionUnderTest -Verifiable -parameterFilter { $param1 -eq "one" }
}

It "Should not throw" {
{ Should -Not -InvokeVerifiable } | Should -Not -Throw
}
}
}

Describe "When Calling Should -Invoke 0 without exactly" {
BeforeAll {
Mock FunctionUnderTest {}
Expand Down