Skip to content

Commit f7b374b

Browse files
nohwndCopilot
andauthored
Print mock history (#2673)
* Print mock history * Polish mock call history and update tests to use -BeLike Refine Format-MockCallHistoryMessage: use [*]/[ ] markers for matching vs non-matching calls, format parameter values with Format-Nicely2, handle zero-call case with <none>. Update Should-Invoke test assertions from exact -Be to -BeLike with wildcard suffix so they tolerate the appended call history text. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Use foreach keyword instead of ForEach-Object in Format-MockCallHistoryMessage Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add dedicated tests for mock call history output format Test the full failure message including history section for: - Filter matches 0 out of 3 calls (all [ ]) - Filter matches 1 out of 3 but expected 2 (mix of [*] and [ ]) - All 3 match but expected 4 (all [*]) - Zero calls (shows <none>) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add mock behavior location to call history output Show file:line of the Mock -MockWith scriptblock in the invocation history so users can identify which behavior handled each call. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Tighten mock history test assertions Spell out markers and command names, only wildcard path prefix and line numbers to avoid brittleness. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Fix merge conflict markers in test file Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 04a729f commit f7b374b

2 files changed

Lines changed: 133 additions & 18 deletions

File tree

src/functions/Mock.ps1

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,7 @@ function Should-InvokeInternal {
470470
if ($matchingCalls.Count -eq $Times -and ($Exactly -or !$PSBoundParameters.ContainsKey('Times'))) {
471471
return [Pester.ShouldResult] @{
472472
Succeeded = $false
473-
FailureMessage = "Expected ${commandName}${moduleMessage} not to be called exactly $Times times,$(Format-Because $Because) but it was"
473+
FailureMessage = "Expected ${commandName}${moduleMessage} not to be called exactly $Times times,$(Format-Because $Because) but it was`n$(Format-MockCallHistoryMessage $callHistory $matchingCalls $nonMatchingCalls)"
474474
ExpectResult = [Pester.ShouldExpectResult]@{
475475
Expected = "${commandName}${moduleMessage} not to be called exactly $Times times"
476476
Actual = "${commandName}${moduleMessage} was called $($matchingCalls.count) times"
@@ -481,7 +481,7 @@ function Should-InvokeInternal {
481481
elseif ($matchingCalls.Count -ge $Times -and !$Exactly) {
482482
return [Pester.ShouldResult] @{
483483
Succeeded = $false
484-
FailureMessage = "Expected ${commandName}${moduleMessage} to be called less than $Times times,$(Format-Because $Because) but was called $($matchingCalls.Count) times"
484+
FailureMessage = "Expected ${commandName}${moduleMessage} to be called less than $Times times,$(Format-Because $Because) but was called $($matchingCalls.Count) times`n$(Format-MockCallHistoryMessage $callHistory $matchingCalls $nonMatchingCalls)"
485485
ExpectResult = [Pester.ShouldExpectResult]@{
486486
Expected = "${commandName}${moduleMessage} to be called less than $Times times"
487487
Actual = "${commandName}${moduleMessage} was called $($matchingCalls.count) times"
@@ -494,7 +494,7 @@ function Should-InvokeInternal {
494494
if ($matchingCalls.Count -ne $Times -and ($Exactly -or ($Times -eq 0))) {
495495
return [Pester.ShouldResult] @{
496496
Succeeded = $false
497-
FailureMessage = "Expected ${commandName}${moduleMessage} to be called $Times times exactly,$(Format-Because $Because) but was called $($matchingCalls.Count) times"
497+
FailureMessage = "Expected ${commandName}${moduleMessage} to be called $Times times exactly,$(Format-Because $Because) but was called $($matchingCalls.Count) times`n$(Format-MockCallHistoryMessage $callHistory $matchingCalls $nonMatchingCalls)"
498498
ExpectResult = [Pester.ShouldExpectResult]@{
499499
Expected = "${commandName}${moduleMessage} to be called $Times times exactly"
500500
Actual = "${commandName}${moduleMessage} was called $($matchingCalls.count) times"
@@ -505,7 +505,7 @@ function Should-InvokeInternal {
505505
elseif ($matchingCalls.Count -lt $Times) {
506506
return [Pester.ShouldResult] @{
507507
Succeeded = $false
508-
FailureMessage = "Expected ${commandName}${moduleMessage} to be called at least $Times times,$(Format-Because $Because) but was called $($matchingCalls.Count) times"
508+
FailureMessage = "Expected ${commandName}${moduleMessage} to be called at least $Times times,$(Format-Because $Because) but was called $($matchingCalls.Count) times`n$(Format-MockCallHistoryMessage $callHistory $matchingCalls $nonMatchingCalls)"
509509
ExpectResult = [Pester.ShouldExpectResult]@{
510510
Expected = "${commandName}${moduleMessage} to be called at least $Times times"
511511
Actual = "${commandName}${moduleMessage} was called $($matchingCalls.count) times"
@@ -516,7 +516,7 @@ function Should-InvokeInternal {
516516
elseif ($filterIsExclusive -and $nonMatchingCalls.Count -gt 0) {
517517
return [Pester.ShouldResult] @{
518518
Succeeded = $false
519-
FailureMessage = "Expected ${commandName}${moduleMessage} to only be called with with parameters matching the specified filter,$(Format-Because $Because) but $($nonMatchingCalls.Count) non-matching calls were made"
519+
FailureMessage = "Expected ${commandName}${moduleMessage} to only be called with with parameters matching the specified filter,$(Format-Because $Because) but $($nonMatchingCalls.Count) non-matching calls were made`n$(Format-MockCallHistoryMessage $callHistory $matchingCalls $nonMatchingCalls)"
520520
ExpectResult = [Pester.ShouldExpectResult]@{
521521
Expected = "${commandName}${moduleMessage} to only be called with with parameters matching the specified filter"
522522
Actual = "${commandName}${moduleMessage} was called $($nonMatchingCalls.Count) times with non-matching parameters"
@@ -1879,3 +1879,41 @@ function Repair-EnumParameters {
18791879

18801880
$sb.ToString()
18811881
}
1882+
1883+
function Format-MockCallHistoryMessage ($callHistory, $matchingCalls, $nonMatchingCalls) {
1884+
if ($null -eq $callHistory -or $callHistory.Count -eq 0) {
1885+
return "Performed invocations:`n <none>"
1886+
}
1887+
1888+
$result = "Performed invocations:"
1889+
foreach ($historyEntry in $callHistory) {
1890+
$params = $historyEntry.BoundParams
1891+
if ($null -ne $params -and $params.Count -gt 0) {
1892+
$parts = foreach ($p in $params.GetEnumerator()) { "-$($p.Key) $(Format-Nicely2 $p.Value)" }
1893+
$paramText = $parts -join " "
1894+
}
1895+
else {
1896+
$paramText = ""
1897+
}
1898+
1899+
$marker = if ($historyEntry -in $matchingCalls) { "[*]" } else { "[ ]" }
1900+
$cmd = $historyEntry.Behavior.CommandName
1901+
1902+
$location = ""
1903+
$sb = $historyEntry.Behavior.ScriptBlock
1904+
if ($null -ne $sb -and $sb.File) {
1905+
$file = $sb.File
1906+
$line = $sb.StartPosition.StartLine
1907+
$location = " from ${file}:${line}"
1908+
}
1909+
1910+
if ($paramText) {
1911+
$result += "`n $marker $cmd $paramText$location"
1912+
}
1913+
else {
1914+
$result += "`n $marker $cmd$location"
1915+
}
1916+
}
1917+
1918+
$result
1919+
}

tst/functions/Mock.Tests.ps1

Lines changed: 90 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Set-StrictMode -Version Latest
1+
Set-StrictMode -Version Latest
22
BeforeAll {
33
$PSDefaultParameterValues = @{ 'Should:ErrorAction' = 'Stop' }
44
function FunctionUnderTest {
@@ -743,7 +743,7 @@ Describe "When Calling Should -Invoke 0 without exactly" {
743743
}
744744

745745
It "Should throw if mock was called" {
746-
$result.Exception.Message | Should -Be 'Expected FunctionUnderTest to be called 0 times exactly, but was called 1 times'
746+
$result.Exception.Message | Should -BeLike 'Expected FunctionUnderTest to be called 0 times exactly, but was called 1 times*'
747747
}
748748

749749
It "Should not throw if mock was not called" {
@@ -757,7 +757,7 @@ Describe "When Calling Should -Invoke 0 without exactly" {
757757
Catch {
758758
$failure = $_
759759
}
760-
$failure.Exception.Message | Should -Be 'Expected FunctionUnderTest to be called 0 times exactly, because of reasons, but was called 1 times'
760+
$failure.Exception.Message | Should -BeLike 'Expected FunctionUnderTest to be called 0 times exactly, because of reasons, but was called 1 times*'
761761
}
762762
}
763763

@@ -775,7 +775,7 @@ Describe "When Calling Should -Not -Invoke without exactly" {
775775
}
776776

777777
It "Should throw if mock was called once" {
778-
$result.Exception.Message | Should -Be "Expected FunctionUnderTest not to be called exactly 1 times, but it was"
778+
$result.Exception.Message | Should -BeLike "Expected FunctionUnderTest not to be called exactly 1 times, but it was*"
779779
}
780780

781781
It "Should not throw if mock was not called" {
@@ -817,7 +817,7 @@ Describe "When Calling Should -Not -Invoke [Times] without exactly" {
817817
$result = $_
818818
}
819819

820-
$result.Exception.Message | Should -Be "Expected FunctionUnderTest to be called less than $Times times, but was called $MockCalls times"
820+
$result.Exception.Message | Should -BeLike "Expected FunctionUnderTest to be called less than $Times times, but was called $MockCalls times*"
821821
}
822822

823823
It 'Should include reason when -Because is used' {
@@ -830,7 +830,7 @@ Describe "When Calling Should -Not -Invoke [Times] without exactly" {
830830
Catch {
831831
$failure = $_
832832
}
833-
$failure.Exception.Message | Should -Be 'Expected FunctionUnderTest to be called less than 1 times, because of reasons, but was called 2 times'
833+
$failure.Exception.Message | Should -BeLike 'Expected FunctionUnderTest to be called less than 1 times, because of reasons, but was called 2 times*'
834834
}
835835
}
836836

@@ -849,7 +849,7 @@ Describe "When Calling Should -Invoke with exactly" {
849849
}
850850

851851
It "Should throw if mock was not called the number of times specified" {
852-
$result.Exception.Message | Should -Be "Expected FunctionUnderTest to be called 3 times exactly, but was called 2 times"
852+
$result.Exception.Message | Should -BeLike "Expected FunctionUnderTest to be called 3 times exactly, but was called 2 times*"
853853
}
854854

855855
It "Should not throw if mock was called the number of times specified" {
@@ -871,7 +871,7 @@ Describe "When Calling Should -Not -Invoke with exactly" {
871871
}
872872

873873
It "Should throw if mock was called" {
874-
$result.Exception.Message | Should -Be "Expected FunctionUnderTest not to be called exactly 1 times, but it was"
874+
$result.Exception.Message | Should -BeLike "Expected FunctionUnderTest not to be called exactly 1 times, but it was*"
875875
}
876876

877877
It "Should not throw if mock was not called" {
@@ -885,7 +885,7 @@ Describe "When Calling Should -Not -Invoke with exactly" {
885885
Catch {
886886
$failure = $_
887887
}
888-
$failure.Exception.Message | Should -Be 'Expected FunctionUnderTest not to be called exactly 1 times, because of reasons, but it was'
888+
$failure.Exception.Message | Should -BeLike 'Expected FunctionUnderTest not to be called exactly 1 times, because of reasons, but it was*'
889889
}
890890
}
891891

@@ -924,7 +924,7 @@ Describe "When Calling Should -Not -Invoke [Times] with exactly" {
924924
$result = $_
925925
}
926926

927-
$result.Exception.Message | Should -Be "Expected FunctionUnderTest not to be called exactly $Times times, but it was"
927+
$result.Exception.Message | Should -BeLike "Expected FunctionUnderTest not to be called exactly $Times times, but it was*"
928928
}
929929
}
930930

@@ -938,7 +938,7 @@ Describe "When Calling Should -Invoke without exactly" {
938938

939939
It "Should throw if mock was not called at least the number of times specified" {
940940
$scriptBlock = { Should -Invoke FunctionUnderTest 4 -Scope Describe }
941-
$scriptBlock | Should -Throw "Expected FunctionUnderTest to be called at least 4 times, but was called 3 times"
941+
$scriptBlock | Should -Throw "Expected FunctionUnderTest to be called at least 4 times, but was called 3 times*"
942942
}
943943

944944
It "Should not throw if mock was called at least the number of times specified" {
@@ -961,7 +961,7 @@ Describe "When Calling Should -Invoke without exactly" {
961961
Catch {
962962
$failure = $_
963963
}
964-
$failure.Exception.Message | Should -Be 'Expected FunctionUnderTest to be called at least 4 times, because of reasons, but was called 3 times'
964+
$failure.Exception.Message | Should -BeLike 'Expected FunctionUnderTest to be called at least 4 times, because of reasons, but was called 3 times*'
965965
}
966966

967967
It 'Should include reason when -Because is used with -ExclusiveFilter' {
@@ -971,7 +971,84 @@ Describe "When Calling Should -Invoke without exactly" {
971971
Catch {
972972
$failure = $_
973973
}
974-
$failure.Exception.Message | Should -Be 'Expected FunctionUnderTest to only be called with with parameters matching the specified filter, because of reasons, but 1 non-matching calls were made'
974+
$failure.Exception.Message | Should -BeLike 'Expected FunctionUnderTest to only be called with with parameters matching the specified filter, because of reasons, but 1 non-matching calls were made*'
975+
}
976+
}
977+
978+
Describe "Mock call history in Should -Invoke failure messages" {
979+
It "Shows all calls as non-matching when filter matches 0 out of 3" {
980+
Mock FunctionUnderTest { }
981+
FunctionUnderTest "one"
982+
FunctionUnderTest "two"
983+
FunctionUnderTest "three"
984+
985+
try {
986+
Should -Invoke FunctionUnderTest -ParameterFilter { $param1 -eq 'four' }
987+
}
988+
catch {
989+
$failure = $_
990+
}
991+
992+
$failure.Exception.Message | Should -BeLike ("Expected FunctionUnderTest*was called 0 times
993+
Performed invocations:
994+
[[] ] FunctionUnderTest -param1 'one' from *Mock.Tests.ps1:*
995+
[[] ] FunctionUnderTest -param1 'two' from *Mock.Tests.ps1:*
996+
[[] ] FunctionUnderTest -param1 'three' from *Mock.Tests.ps1:*" -replace "`r`n", "`n")
997+
}
998+
999+
It "Shows matching and non-matching calls when filter matches 1 out of 3 but expected 2" {
1000+
Mock FunctionUnderTest { }
1001+
FunctionUnderTest "one"
1002+
FunctionUnderTest "two"
1003+
FunctionUnderTest "one"
1004+
1005+
try {
1006+
Should -Invoke FunctionUnderTest -Exactly 2 -ParameterFilter { $param1 -eq 'two' }
1007+
}
1008+
catch {
1009+
$failure = $_
1010+
}
1011+
1012+
$failure.Exception.Message | Should -BeLike ("Expected FunctionUnderTest*was called 1 times*
1013+
Performed invocations:
1014+
[[] ] FunctionUnderTest -param1 'one' from *Mock.Tests.ps1:*
1015+
[[]*] FunctionUnderTest -param1 'two' from *Mock.Tests.ps1:*
1016+
[[] ] FunctionUnderTest -param1 'one' from *Mock.Tests.ps1:*" -replace "`r`n", "`n")
1017+
}
1018+
1019+
It "Shows all calls as matching when all 3 match but expected 4" {
1020+
Mock FunctionUnderTest { }
1021+
FunctionUnderTest "one"
1022+
FunctionUnderTest "one"
1023+
FunctionUnderTest "one"
1024+
1025+
try {
1026+
Should -Invoke FunctionUnderTest -Exactly 4 -ParameterFilter { $param1 -eq 'one' }
1027+
}
1028+
catch {
1029+
$failure = $_
1030+
}
1031+
1032+
$failure.Exception.Message | Should -BeLike ("Expected FunctionUnderTest*was called 3 times*
1033+
Performed invocations:
1034+
[[]*] FunctionUnderTest -param1 'one' from *Mock.Tests.ps1:*
1035+
[[]*] FunctionUnderTest -param1 'one' from *Mock.Tests.ps1:*
1036+
[[]*] FunctionUnderTest -param1 'one' from *Mock.Tests.ps1:*" -replace "`r`n", "`n")
1037+
}
1038+
1039+
It 'Shows empty marker when mock was never called' {
1040+
Mock FunctionUnderTest { }
1041+
1042+
try {
1043+
Should -Invoke FunctionUnderTest -Exactly 1
1044+
}
1045+
catch {
1046+
$failure = $_
1047+
}
1048+
1049+
$failure.Exception.Message | Should -Be ('Expected FunctionUnderTest to be called 1 times exactly, but was called 0 times
1050+
Performed invocations:
1051+
<none>' -replace "`r`n", "`n")
9751052
}
9761053
}
9771054

0 commit comments

Comments
 (0)