test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

3 participants

@Evangelink@0101
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

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 > 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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

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

test: add edge case tests for MSTEST0020/0021 analyzers - #9669

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests
Jul 7, 2026
Merged

test: add edge case tests for MSTEST0020/0021 analyzers#9669
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
dev/amauryleve/mstest0020-0021-edge-case-tests

Conversation

@Evangelink

Copy link
Copy Markdown
Member

What

Adds 3 edge-case tests documenting existing behavior of the "prefer OOP pattern" analyzers and their code fixers. No production code changes — purely additive tests.

PreferConstructorOverTestInitializeAnalyzerTests (MSTEST0020) — +2 tests

TestCovers
WhenTestInitializeMethodInNonTestClass_DiagnosticAnalyzer fires on [TestInitialize] even when the containing class lacks [TestClass]; fixer replaces the method with a constructor carrying its body
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtorFixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized (no default constructor exists)

PreferDisposeOverTestCleanupAnalyzerTests (MSTEST0021) — +1 test

TestCovers
WhenTestCleanupMethodInNonTestClass_DiagnosticAnalyzer fires on [TestCleanup] even when the containing class lacks [TestClass]; fixer adds IDisposable to the base list and creates a Dispose() method

Why

Both PreferConstructorOverTestInitializeAnalyzer and PreferDisposeOverTestCleanupAnalyzer fire whenever [TestInitialize]/[TestCleanup] is present on a qualifying method, regardless of whether the containing class has [TestClass], and the MSTEST0020 fixer merges into the first non-static constructor even if parameterized. These behaviors were previously undocumented in tests.

Test status

Debug build clean (0 warnings, 0 errors). MSTest.Analyzers.UnitTests passes on both net472 and net8.0.

This consolidates the near-duplicate test-improver contributions from #9663 and #9648 into a single coherent set, addressing the review item tracked in #9597.

Fixes#9597
Fixes#9663
Fixes#9648

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Add 3 edge-case tests documenting existing analyzer/fixer behavior:
PreferConstructorOverTestInitializeAnalyzer (MSTEST0020):
- WhenTestInitializeMethodInNonTestClass_Diagnostic: analyzer fires on a [TestInitialize] method in a class without [TestClass]; fixer replaces it with a constructor carrying its body.
- WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_CodeFix_MergesIntoParameterizedCtor: fixer merges the body into the first non-static constructor even when it is parameterized.
PreferDisposeOverTestCleanupAnalyzer (MSTEST0021):
- WhenTestCleanupMethodInNonTestClass_Diagnostic: analyzer fires on a [TestCleanup] method in a class without [TestClass]; fixer adds IDisposable and creates a Dispose() method.
Fixes#9597, #9663, #9648
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings July 7, 2026 03:48
@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jul 7, 2026
@Evangelink
Amaury Levé (Evangelink) enabled auto-merge (squash) July 7, 2026 03:49

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.

Pull request overview

This PR adds three edge-case unit tests to the MSTest.Analyzers.UnitTests project that document previously-untested behavior of the "prefer OOP pattern" analyzers (MSTEST0020 PreferConstructorOverTestInitialize and MSTEST0021 PreferDisposeOverTestCleanup) and their code fixers. It is purely additive test coverage with no production code changes, consolidating the near-duplicate contributions from #9663 and #9648.

Changes:

  • Adds tests confirming both analyzers fire on [TestInitialize]/[TestCleanup] even when the containing class lacks [TestClass] (no guard), including verifying the resulting code fixes.
  • Adds a test confirming the MSTEST0020 fixer merges the [TestInitialize] body into the first non-static constructor even when it is parameterized and no default constructor exists.
Show a summary per file
FileDescription
test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.csAdds 2 tests: non-[TestClass] diagnostic + constructor fix, and merge-into-parameterized-ctor fix.
test/UnitTests/MSTest.Analyzers.UnitTests/PreferDisposeOverTestCleanupAnalyzerTests.csAdds 1 test: non-[TestClass] diagnostic + IDisposable/Dispose() fix.

Review details

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Medium

@github-actionsgithub-actionsBot 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.

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.

✅ 22/22 dimensions clean — no findings.

Summary: Purely additive edge-case tests documenting existing analyzer behavior. Tests follow established patterns (VerifyCS.VerifyCodeFixAsync), use proper naming conventions (When<Condition>_<Outcome>), have no shared state (test isolation clean), are deterministic (no flakiness risk), and respect the project's BannedSymbols.txt policy. Comments correctly explain the "why" (documenting undocumented behavior). PR scope is focused — single concern, no production code mixed in.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test quality grade — PR #9669

GradeTestNotes
B (80–89)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestClassHasOnlyParameterizedCtorAndTestInitialize_
CodeFix_
MergesIntoParameterizedCtor
43-line body exceeds the ~30-line threshold; length is driven by multi-line test-data strings — consider a shared helper if similar scenarios multiply.
A (90–100)new PreferConstructorOverTestInitializeAnalyzerTests.
WhenTestInitializeMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.
A (90–100)new PreferDisposeOverTestCleanupAnalyzerTests.
WhenTestCleanupMethodInNonTestClass_
Diagnostic
Clear AAA; VerifyCodeFixAsync validates both the diagnostic marker and the full code-fix transformation. No issues found.

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. · 63 AIC · ⌖ 7.4 AIC · ⊞ 9.5K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 621b8ff into mainJul 7, 2026
41 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the dev/amauryleve/mstest0020-0021-edge-case-tests branch July 7, 2026 04:46
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

3 participants

@Evangelink@0101