Keep the error location when the error comes from inside Pester, and name the assertion - #2981
Merged
nohwnd merged 2 commits intoAug 15, 2026
Merged
Conversation
Fix pester#2980 ConvertTo-FailureLines stopped walking the stack trace on the first frame that is inside Pester, with an exemption list of Should<End> and Invoke-Assertion. In 6 the assertions are Should-* functions with their own internal helpers, and Mock and the rest of the runtime were never on that list, so the loop broke on the very first frame and the whole trace was dropped, including the line in the test file. Skip the Pester frames instead of stopping on them, and stop on the first Pester frame that comes after user code. That is positional, so it does not need to know the names of the internal helpers.
Fix pester#2977 The stack trace no longer shows Should-Be, because the frames inside Pester are skipped, so the message has to say which assertion refused the collection. Ensure-ExpectedIsNotCollection takes the name, EnsureScalar passes the caller of the assertion, Should-BeSame and Should-NotBeSame pass their own name because they call the guard directly. Tests for the trace of an error thrown from inside Pester, for the call chain of an assertion that fails in a function, for not printing the same file and line twice, and for the name in the message. Format-ErrorMessage tests had the line number of the 1/0 in them, read it from the error record instead so adding tests above does not break them.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix #2980
Fix #2977
Two changes, they belong together.
1, 2, 3 | Should-Be 1, 2, 3said "this assertion" and printed no location at all. The location part is the wider one, any error thrown from inside a Pester function lost its whole stack trace,Mockfor a command that does not exist had the same problem.demo.tests.ps1:Before, on 6.1.0 with the default
StackTraceVerbosity:Three of the four have no location. The fourth points at the assertion on line 13, but not at line 15 that called the function, so in a bigger suite you do not know which of the calls to
Test-Thingfailed.After:
Every line number points at the line in the file above that caused the failure.
Why?
ConvertTo-FailureLineswalked the trace and broke on the first frame that was inside Pester, withShould<End>|Invoke-Assertionas the only exemption:That exemption is Pester 4 and 5. This is the trace of the first test,
StackTraceVerbosity = Full:The first frame is Pester and it is not
Should<End>, so we broke on it and the trace came out empty, one line above the one we wanted.StackTraceVerbosity = Fullwas the only way to see it, and then you get all 28 frames.Assertion failures kept their line only because of the special case above the loop, which builds it from
TargetObjectwhenFullyQualifiedErrorIdisPesterAssertionFailed. Everything else lost the location.Adding
Ensure-ExpectedIsNotCollectionto the exemption list fixesShould-Beand breaks again on the next internal helper we write. The rule is positional now instead. Skip Pester frames until the first frame that comes from user code, stop on the first Pester frame after that. Applied to the trace above it skips the first three, keepsdemo.tests.ps1:3, and stops onPester.psm1:2068.Since the trace does not show
Should-Beanymore either, the message has to name the assertion.Ensure-ExpectedIsNotCollectiontakes a name,EnsureScalarpasses$this.Caller, which is the$PSCmdletof the assertion, so every assertion that guards a scalar names itself with nothing to do per assertion.Should-BeSameandShould-NotBeSamepass their own name because they call the guard directly and not through$assert.Behavior change
The fourth test is the one to look at. An assertion that fails inside a helper function now prints the call as a second line, which is what the comment in the loop already asked for:
When the assertion sits directly in the
Itnothing changes. The frame from the trace is then the same file and line as the line built fromTargetObject, and it is skipped so the location is not printed twice.Tests
Output.Tests.ps1gets four: an error thrown from inside Pester keeps the line from the test file, the frames inside Pester are dropped, an assertion that fails in a function produces both the assertion line and the call, and the same file and line is not printed twice when the assertion is directly in the file.Ensure-ExpectedIsNotCollection.Tests.ps1gets one for the name in the message.The
Format-ErrorMessagetests had the line number of the1 / 0written into them as385, adding tests above them moved it. They read it from the error record now.Full suite passes locally, 2896 tests.
🤖