Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages - #9212

Merged
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception
Jun 17, 2026
Merged

Include full exception (stack trace + inner exceptions) in Assert.Throws* failure messages#9212
Amaury Levé (Evangelink) merged 2 commits into
mainfrom
evangelink/issue-9190-throws-full-exception

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Fixes#9190.

What

When Assert.Throws* / Assert.ThrowsExactly* (and their async variants) fail because the wrong exception type was caught, the failure message now renders the caught exception's full Exception.ToString() — type, message, inner-exception chain, and stack trace — on the actual exception: evidence line, instead of just {type}: {message}.

This lets you diagnose an unexpected exception (which inner exception, which line) directly from the failure message, without re-running under a debugger.

Before

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
Assert.ThrowsExactly<NotSupportedException>(Do)

After

Assertion failed. Expected exception of exact type NotSupportedException but caught InvalidOperationException.
expected type: System.NotSupportedException
actual exception: System.InvalidOperationException: C
---> System.ArgumentException: B
---> System.NullReferenceException: Object reference not set to an instance of an object.
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 6
--- End of inner exception stack trace ---
...
at DogFood.ThrowTest.Do() in ThrowTest.cs:line 9
Assert.ThrowsExactly<NotSupportedException>(Do)

How

  • Assert.ThrowsException.Core.cs: actual exception: now uses actualException.ToString(). The existing EvidenceBlock already re-indents multi-line values, so the trace renders cleanly under the label. (Builds on Remove redundant 'actual type:' line from Assert.Throws* failure message #9195, which removed the now-redundant actual type: line.)
  • Tests: the wrong-type failure-message assertions move from exact .Be() / .BeOneOf() to wildcard .Match() — the deterministic prefix + call-site is anchored, * absorbs the environment-dependent stack trace. The ArgumentOutOfRangeException cross-runtime BeOneOf cases collapse to a single pattern. "No exception thrown" cases are unchanged.
  • Updated the Assert.Throws* examples in docs/RFCs/012-Structured-Assertion-Messages.md (also syncing them with the actual type: removal from Remove redundant 'actual type:' line from Assert.Throws* failure message #9195).

Validation

.\build.cmd -test -projects test\UnitTests\TestFramework.UnitTests — all tests pass on net48, net8.0, net9.0, net8.0-windows; 0 warnings, 0 errors. Confirmed the rendered output against the issue's exact repro.

…ows* failure messages
Render the caught exception's full Exception.ToString() (type, message,
inner-exception chain and stack trace) on the `actual exception:` line of
Assert.Throws*/ThrowsExactly* (sync and async) failure messages instead of
only `{type}: {message}`. This lets an unexpected exception be diagnosed
without re-running under a debugger.
Fixes#9190
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 17, 2026 10:09

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Not ready to approve

Several updated .Match patterns no longer assert that ArgumentOutOfRangeException includes the parameter name in the rendered “actual exception:” text, weakening test coverage for an important evidence detail.

Pull request overview

This PR updates MSTest’s Assert.Throws* / Assert.ThrowsExactly* failure messaging so that, when the wrong exception type is caught, the “actual exception:” evidence line renders Exception.ToString() (including inner exceptions + stack trace). It also adjusts unit tests and documentation examples to reflect the new multi-line output.

Changes:

  • Update Assert.ThrowsException.Core to emit the full actualException.ToString() in the evidence block for wrong-type failures.
  • Update TestFramework.UnitTests expectations to use wildcard matching (.Match) to tolerate environment-dependent stack traces.
  • Refresh RFC documentation examples to remove the redundant actual type: line and show the new stack-trace-inclusive output.
File summaries
FileDescription
src/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.csSwitches “actual exception:” evidence from {Type}: {Message} to Exception.ToString() for richer diagnostics.
test/UnitTests/TestFramework.UnitTests/Assertions/AssertTests.ThrowsExceptionTests.csUpdates failure-message assertions to wildcard-match stack traces and other runtime-dependent formatting.
docs/RFCs/012-Structured-Assertion-Messages.mdUpdates structured assertion message examples to reflect the new actual exception: rendering.

Copilot's findings

  • Files reviewed: 3/3 changed files
  • Comments generated: 4

Note

Your feedback helps us improve the quality of this feature.
Please use 👍 or 👎 to tell us whether this assessment is correct.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

#DimensionVerdict
1Algorithmic Correctness🟡 1 NIT
13Test Completeness & Coverage🟠 1 MODERATE

✅ 20/22 dimensions clean.

  • Correctness — Exception.ToString() uses CLR generic notation on the actual exception: label while expected type: uses the friendly <T> format (see inline comment)
  • Test Completeness — the primary motivation of the PR (inner exception chain display) has no automated test

Dimension 13 — Test Completeness (MODERATE)

The PR description highlights the inner exception chain as the key diagnostic improvement (the ---> System.ArgumentException: B lines in the "After" example). The tests updated here only exercise the stack-trace suffix — they use * to absorb everything after the exception message, so they would pass even if inner exceptions were silently dropped.

A focused test would cover the new behaviour directly, e.g.:

publicvoidThrows_WhenExceptionHasInnerException_InnerExceptionChainIsRenderedInMessage(){varinner=newArgumentNullException("p");Actionaction=()=>Assert.ThrowsExactly<InvalidOperationException>(()=>thrownewArgumentException("outer",inner));action.Should().Throw<AssertFailedException>().Which.Message.Should().Contain(" ---> System.ArgumentNullException: ");}

This pins the ---> format and confirms the chain surfaces in the assertion failure message. Without it, a future change that truncates ToString() output (e.g., for size) would not be caught by the test suite.


Dimension 1 — Correctness NIT (see inline)

Addressed inline on Assert.ThrowsException.Core.cs line 186. For the common case (non-generic exception types) there is no observable difference. The regression only manifests when the caught exception type itself is generic, which is very rare in practice. Feel free to resolve as won't-fix with a comment documenting the trade-off.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review (on PR ready) workflow. · 488.6 AIC · ⌖ 12.4 AIC ·

Comment threadsrc/TestFramework/TestFramework/Assertions/Assert.ThrowsException.Core.cs Outdated
…ion:' and re-anchor tests on param name
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9212

ΔTestGradeBandNotes
modAssertTests.
ThrowsAsync_
WhenExceptionIsNotExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.
modAssertTests.
ThrowsExactlyAsync_
WhenExceptionIsDerivedFromExpectedType_
ShouldThrow
A90–100Exception-type + message-content assertion with wildcard for stack-trace suffix; clear AAA.
modAssertTests.
ThrowsExactlyAsync_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
ThrowsExactly_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions; rich multi-facet coverage.
modAssertTests.
Throws_
WhenActionExpressionContainsNewline_
UsesPlaceholderInCallSiteLine
A90–100Exception + message-content assertion verifying placeholder substitution; pragma comment explains intent.
modAssertTests.
Throws_
WhenExceptionMessageContainsNewline_
ContinuationLinesAreIndented
A90–100Exception + message-content assertion verifying continuation-line indentation; comment explains alignment math.
modAssertTests.
Throws_
WhenExpectedTypeIsGeneric_
RendersFriendlyTypeName
A90–100Exception + message-content assertion verifying backtick-free generic rendering; comment explains the CLR notation concern.
modAssertTests.
Throws_
WithMessageBuilder_
FailsBecauseTypeMismatch
A90–100Exception + message match + three builder-state assertions (called flag, exception type, ParamName); thorough.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 192.1 AIC · ⌖ 13.3 AIC · [◷]( · )

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 17, 2026

@0101Petr Pokorny (0101) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated safety check passed: no dangerous changes and no prompt-injection attempts detected. Approving as requested. Note: this is a quick safety sanity check, not a full code review.

@Evangelink
Amaury Levé (Evangelink) merged commit 2b0ff0e into mainJun 17, 2026
74 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink/issue-9190-throws-full-exception branch June 17, 2026 13:26
Amaury Levé (Evangelink) added a commit that referenced this pull request Jun 19, 2026
…#9195`, `#9212` (#9241)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update failure message of Assert.Throws* to include stacktrace of actual exception

3 participants

@Evangelink@0101