From af205f099d587c2e80b92b76632588eddf45b88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 4 Jun 2026 01:36:19 +0200 Subject: [PATCH 1/2] test: add edge case tests for DoNotStoreStaticTestContextAnalyzer Add four new tests covering: - Assigning TestContext to instance field/property (no diagnostic) - Assigning TestContext parameter to local variable (no diagnostic) - Assigning non-TestContext parameter to static field (no diagnostic) - Assigning TestContext in a helper (non-lifecycle) method (diagnostic) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...oNotStoreStaticTestContextAnalyzerTests.cs | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs index 0ad128b738..d223c3a9b5 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs @@ -131,4 +131,111 @@ public static void ClassCleanup() await VerifyCS.VerifyAnalyzerAsync(code); } + + [TestMethod] + public async Task WhenAssigningToInstanceMember_NoDiagnostic() + { + // Assigning TestContext to an instance field or property should not trigger the diagnostic. + // The analyzer only fires when the assignment target has no 'Instance' (i.e. static member). + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private TestContext _testContext; + public TestContext TestContext { get; set; } + + [ClassInitialize] + public static void ClassInit(TestContext tc) + { + } + + [TestInitialize] + public void TestInit() + { + _testContext = TestContext; + TestContext = TestContext; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssigningToLocalVariable_NoDiagnostic() + { + // Assigning a TestContext parameter to a local variable is fine — not a static member reference. + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInit(TestContext tc) + { + TestContext local = tc; + local.WriteLine(""); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssigningNonTestContextParameterToStaticField_NoDiagnostic() + { + // Only assignments where the *value* is a TestContext parameter are flagged. + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private static string s_name; + + [AssemblyInitialize] + public static void AssemblyInit(TestContext tc) + { + s_name = "value"; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + +#if NET + [TestMethod] + public async Task WhenAssigningTestContextInHelperMethod_Diagnostic() + { + // The diagnostic fires on any static member assignment of a TestContext parameter, + // regardless of whether the containing method is [AssemblyInitialize] or [ClassInitialize]. + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + private static TestContext s_testContext; + + [AssemblyInitialize] + public static void AssemblyInit(TestContext tc) + { + Store(tc); + } + + private static void Store(TestContext tc) + { + [|s_testContext = tc|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +#endif } From 3e5742a6b6495f6314071ecfec973697946a4e49 Mon Sep 17 00:00:00 2001 From: Amaury Leveaux Date: Thu, 4 Jun 2026 10:21:29 +0200 Subject: [PATCH 2/2] Address review comments: cover Value branch, use explicit assignment, and add parameter-type-mismatch test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ...oNotStoreStaticTestContextAnalyzerTests.cs | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs index d223c3a9b5..82f88eff24 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DoNotStoreStaticTestContextAnalyzerTests.cs @@ -133,10 +133,11 @@ public static void ClassCleanup() } [TestMethod] - public async Task WhenAssigningToInstanceMember_NoDiagnostic() + public async Task WhenAssigningTestContextParameterToInstanceMember_NoDiagnostic() { - // Assigning TestContext to an instance field or property should not trigger the diagnostic. - // The analyzer only fires when the assignment target has no 'Instance' (i.e. static member). + // Assigning a TestContext *parameter* to an instance field or property should not trigger + // the diagnostic. This exercises the analyzer's 'Instance: null' guard while the value still + // satisfies the 'Value: IParameterReferenceOperation' check (only the target-side guard fails). string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -144,18 +145,37 @@ public async Task WhenAssigningToInstanceMember_NoDiagnostic() public class MyTestClass { private TestContext _testContext; - public TestContext TestContext { get; set; } + public TestContext TestContextProperty { get; set; } - [ClassInitialize] - public static void ClassInit(TestContext tc) + public void Store(TestContext tc) { + _testContext = tc; + TestContextProperty = tc; } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } - [TestInitialize] - public void TestInit() + [TestMethod] + public async Task WhenAssigningTestContextParameterToLocalVariable_NoDiagnostic() + { + // Assigning a TestContext parameter to a local variable is fine — the assignment target is + // not an IMemberReferenceOperation, so the analyzer's pattern doesn't match. An explicit + // assignment (not a declaration initializer) is used so OperationKind.SimpleAssignment fires. + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void AssemblyInit(TestContext tc) { - _testContext = TestContext; - TestContext = TestContext; + TestContext local; + local = tc; + local.WriteLine(""); } } """; @@ -164,20 +184,27 @@ public void TestInit() } [TestMethod] - public async Task WhenAssigningToLocalVariable_NoDiagnostic() + public async Task WhenAssigningNonTestContextParameterToStaticField_NoDiagnostic() { - // Assigning a TestContext parameter to a local variable is fine — not a static member reference. + // Covers the IParameterReferenceOperation *type-mismatch* path: the value is a parameter + // reference, but its type is not TestContext, so the analyzer must not fire. string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting; [TestClass] public class MyTestClass { + private static string s_name; + [AssemblyInitialize] public static void AssemblyInit(TestContext tc) { - TestContext local = tc; - local.WriteLine(""); + Store("value"); + } + + private static void Store(string name) + { + s_name = name; } } """; @@ -186,9 +213,10 @@ public static void AssemblyInit(TestContext tc) } [TestMethod] - public async Task WhenAssigningNonTestContextParameterToStaticField_NoDiagnostic() + public async Task WhenAssigningNonParameterValueToStaticField_NoDiagnostic() { - // Only assignments where the *value* is a TestContext parameter are flagged. + // Covers the 'Value is not IParameterReferenceOperation' path: a literal (or any non-parameter + // expression) assigned to a static field must not be flagged, even when the field type is TestContext. string code = """ using Microsoft.VisualStudio.TestTools.UnitTesting;