From 5f1bc1e0517721cb1033a98b5bea00245cdd2c6c Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Tue, 11 Jun 2024 22:57:21 -0700 Subject: [PATCH 1/2] Refactor test cases to matrix on Moq version --- .../AsAcceptOnlyInterfaceAnalyzerTests.cs | 18 ++-- ...tureShouldMatchMockedMethodCodeFixTests.cs | 87 +++++-------------- ...ructorArgumentsShouldMatchAnalyzerTests.cs | 85 +++++++++--------- .../Helpers/AnalyzerVerifier.cs | 8 +- .../Helpers/CodeFixVerifier.cs | 6 +- .../Helpers/ReferenceAssemblyCatalog.cs | 18 ++-- Source/Moq.Analyzers.Test/Helpers/Test.cs | 1 - .../Helpers/TestDataExtensions.cs | 22 +++++ ...rArgumentsForInterfaceMockAnalyzerTests.cs | 36 ++++---- .../NoMethodsInPropertySetupAnalyzerTests.cs | 23 ++--- .../NoSealedClassMocksAnalyzerTests.cs | 13 +-- ...dOnlyForOverridableMembersAnalyzerTests.cs | 25 +++--- ...houldNotIncludeAsyncResultAnalyzerTests.cs | 15 ++-- 13 files changed, 177 insertions(+), 180 deletions(-) create mode 100644 Source/Moq.Analyzers.Test/Helpers/TestDataExtensions.cs diff --git a/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs b/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs index e41fac671..fd7f54edd 100644 --- a/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/AsAcceptOnlyInterfaceAnalyzerTests.cs @@ -1,3 +1,4 @@ +using Microsoft.CodeAnalysis.Testing; using Verifier = Moq.Analyzers.Test.Helpers.AnalyzerVerifier; namespace Moq.Analyzers.Test; @@ -6,20 +7,20 @@ public class AsAcceptOnlyInterfaceAnalyzerTests { public static IEnumerable TestData() { - foreach (var @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { // TODO: .As and .As feels redundant - yield return [@namespace, """new Mock().As<{|Moq1300:BaseSampleClass|}>();"""]; - yield return [@namespace, """new Mock().As<{|Moq1300:SampleClass|}>();"""]; - yield return [@namespace, """new Mock().As();"""]; + ["""new Mock().As<{|Moq1300:BaseSampleClass|}>();"""], + ["""new Mock().As<{|Moq1300:SampleClass|}>();"""], + ["""new Mock().As();"""], // TODO: Testing with .Setup() and .Returns() seems unnecessary. - yield return [@namespace, """new Mock().As().Setup(x => x.Calculate(It.IsAny(), It.IsAny())).Returns(10);"""]; - } + ["""new Mock().As().Setup(x => x.Calculate(It.IsAny(), It.IsAny())).Returns(10);"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShouldAnalyzeAs(string @namespace, string mock) + public async Task ShouldAnalyzeAs(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -48,6 +49,7 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } } diff --git a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs index 80e1292a4..af3280c0d 100644 --- a/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs +++ b/Source/Moq.Analyzers.Test/CallbackSignatureShouldMatchMockedMethodCodeFixTests.cs @@ -6,125 +6,78 @@ public class CallbackSignatureShouldMatchMockedMethodCodeFixTests { public static IEnumerable TestData() { - foreach (string @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { - yield return [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny())).Returns((string s) => { return 0; });""", """new Mock().Setup(x => x.Do(It.IsAny())).Returns((string s) => { return 0; });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns((int i, string s, DateTime dt) => { return 0; });""", """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns((int i, string s, DateTime dt) => { return 0; });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; });""", """new Mock().Setup(x => x.Do(It.IsAny>())).Returns((List l) => { return 0; });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny())).Callback({|Moq1100:(int i)|} => { });""", """new Mock().Setup(x => x.Do(It.IsAny())).Callback((string s) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny())).Callback({|Moq1100:(string s1, string s2)|} => { });""", """new Mock().Setup(x => x.Do(It.IsAny())).Callback((string s) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback({|Moq1100:(string s1, int i1)|} => { });""", """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny>())).Callback({|Moq1100:(int i)|} => { });""", """new Mock().Setup(x => x.Do(It.IsAny>())).Callback((List l) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny())).Callback((string s) => { });""", """new Mock().Setup(x => x.Do(It.IsAny())).Callback((string s) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { });""", """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback((int i, string s, DateTime dt) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny>())).Callback((List l) => { });""", """new Mock().Setup(x => x.Do(It.IsAny>())).Callback((List l) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny())).Callback(() => { });""", """new Mock().Setup(x => x.Do(It.IsAny())).Callback(() => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback(() => { });""", """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Callback(() => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny>())).Callback(() => { });""", """new Mock().Setup(x => x.Do(It.IsAny>())).Callback(() => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny())).Returns(0).Callback((string s) => { });""", """new Mock().Setup(x => x.Do(It.IsAny())).Returns(0).Callback((string s) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0).Callback((int i, string s, DateTime dt) => { });""", """new Mock().Setup(x => x.Do(It.IsAny(), It.IsAny(), It.IsAny())).Returns(0).Callback((int i, string s, DateTime dt) => { });""", - ]; - - yield return + ], [ - @namespace, """new Mock().Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { });""", """new Mock().Setup(x => x.Do(It.IsAny>())).Returns(0).Callback((List l) => { });""", - ]; - } + ], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShouldSuggestQuickFixWhenIncorrectCallbacks(string @namespace, string original, string quickFix) + public async Task ShouldSuggestQuickFixWhenIncorrectCallbacks(string referenceAssemblyGroup, string @namespace, string original, string quickFix) { static string Template(string ns, string mock) => $$""" @@ -148,6 +101,6 @@ private void Test() } """; - await Verifier.VerifyCodeFixAsync(Template(@namespace, original), Template(@namespace, quickFix)); + await Verifier.VerifyCodeFixAsync(Template(@namespace, original), Template(@namespace, quickFix), referenceAssemblyGroup); } } diff --git a/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs b/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs index 0c31fd3c1..f7dcf990a 100644 --- a/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/ConstructorArgumentsShouldMatchAnalyzerTests.cs @@ -6,53 +6,53 @@ public class ConstructorArgumentsShouldMatchAnalyzerTests { public static IEnumerable TestData() { - foreach (var @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { - yield return [@namespace, """new Mock(MockBehavior.Default);"""]; - yield return [@namespace, """new Mock(MockBehavior.Strict);"""]; - yield return [@namespace, """new Mock(MockBehavior.Loose);"""]; - yield return [@namespace, """new Mock("3");"""]; - yield return [@namespace, """new Mock("4");"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, "5");"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, "6");"""]; - yield return [@namespace, """new Mock(false, 0);"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, true, 1);"""]; - yield return [@namespace, """new Mock(DateTime.Now, DateTime.Now);"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, DateTime.Now, DateTime.Now);"""]; - yield return [@namespace, """new Mock(new List(), "7");"""]; - yield return [@namespace, """new Mock(new List());"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, new List(), "8");"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, new List());"""]; - yield return [@namespace, """new Mock{|Moq1002:(1, true)|};"""]; - yield return [@namespace, """new Mock{|Moq1002:(2, true)|};"""]; - yield return [@namespace, """new Mock{|Moq1002:("1", 3)|};"""]; - yield return [@namespace, """new Mock{|Moq1002:(new int[] { 1, 2, 3 })|};"""]; - yield return [@namespace, """new Mock{|Moq1002:(MockBehavior.Strict, 4, true)|};"""]; - yield return [@namespace, """new Mock{|Moq1002:(MockBehavior.Loose, 5, true)|};"""]; - yield return [@namespace, """new Mock{|Moq1002:(MockBehavior.Loose, "2", 6)|};"""]; - yield return [@namespace, """new Mock>{|Moq1002:("42")|};"""]; - yield return [@namespace, """new Mock>{|Moq1002:("42", 42)|};"""]; - yield return [@namespace, """new Mock>{|Moq1002:(42)|};"""]; - yield return [@namespace, """new Mock>();"""]; - yield return [@namespace, """new Mock>(MockBehavior.Default);"""]; + ["""new Mock(MockBehavior.Default);"""], + ["""new Mock(MockBehavior.Strict);"""], + ["""new Mock(MockBehavior.Loose);"""], + ["""new Mock("3");"""], + ["""new Mock("4");"""], + ["""new Mock(MockBehavior.Default, "5");"""], + ["""new Mock(MockBehavior.Default, "6");"""], + ["""new Mock(false, 0);"""], + ["""new Mock(MockBehavior.Default, true, 1);"""], + ["""new Mock(DateTime.Now, DateTime.Now);"""], + ["""new Mock(MockBehavior.Default, DateTime.Now, DateTime.Now);"""], + ["""new Mock(new List(), "7");"""], + ["""new Mock(new List());"""], + ["""new Mock(MockBehavior.Default, new List(), "8");"""], + ["""new Mock(MockBehavior.Default, new List());"""], + ["""new Mock{|Moq1002:(1, true)|};"""], + ["""new Mock{|Moq1002:(2, true)|};"""], + ["""new Mock{|Moq1002:("1", 3)|};"""], + ["""new Mock{|Moq1002:(new int[] { 1, 2, 3 })|};"""], + ["""new Mock{|Moq1002:(MockBehavior.Strict, 4, true)|};"""], + ["""new Mock{|Moq1002:(MockBehavior.Loose, 5, true)|};"""], + ["""new Mock{|Moq1002:(MockBehavior.Loose, "2", 6)|};"""], + ["""new Mock>{|Moq1002:("42")|};"""], + ["""new Mock>{|Moq1002:("42", 42)|};"""], + ["""new Mock>{|Moq1002:(42)|};"""], + ["""new Mock>();"""], + ["""new Mock>(MockBehavior.Default);"""], // TODO: "I think this _should_ fail, but currently passes. Tracked by #55." - // yield return [@namespace, """new Mock();"""]; - yield return [@namespace, """new Mock{|Moq1002:("42")|};"""]; - yield return [@namespace, """new Mock{|Moq1002:("42", 42)|};"""]; - yield return [@namespace, """new Mock{|Moq1002:(42)|};"""]; - yield return [@namespace, """new Mock();"""]; - yield return [@namespace, """new Mock(42);"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, 42);"""]; - yield return [@namespace, """new Mock(42, "42");"""]; - yield return [@namespace, """new Mock(MockBehavior.Default, 42, "42");"""]; - yield return [@namespace, """new Mock>(42);"""]; - yield return [@namespace, """new Mock>(MockBehavior.Default, 42);"""]; - } + // ["""new Mock();"""], + ["""new Mock{|Moq1002:("42")|};"""], + ["""new Mock{|Moq1002:("42", 42)|};"""], + ["""new Mock{|Moq1002:(42)|};"""], + ["""new Mock();"""], + ["""new Mock(42);"""], + ["""new Mock(MockBehavior.Default, 42);"""], + ["""new Mock(42, "42");"""], + ["""new Mock(MockBehavior.Default, 42, "42");"""], + ["""new Mock>(42);"""], + ["""new Mock>(MockBehavior.Default, 42);"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShouldAnalyzeConstructorArguments(string @namespace, string mock) + public async Task ShouldAnalyzeConstructorArguments(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -95,6 +95,7 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } } diff --git a/Source/Moq.Analyzers.Test/Helpers/AnalyzerVerifier.cs b/Source/Moq.Analyzers.Test/Helpers/AnalyzerVerifier.cs index 5f371e074..548f1c977 100644 --- a/Source/Moq.Analyzers.Test/Helpers/AnalyzerVerifier.cs +++ b/Source/Moq.Analyzers.Test/Helpers/AnalyzerVerifier.cs @@ -1,5 +1,4 @@ -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Testing; namespace Moq.Analyzers.Test.Helpers; @@ -7,12 +6,15 @@ namespace Moq.Analyzers.Test.Helpers; internal static class AnalyzerVerifier where TAnalyzer : DiagnosticAnalyzer, new() { - public static async Task VerifyAnalyzerAsync(string source) + public static async Task VerifyAnalyzerAsync(string source, string referenceAssemblyGroup) { + ReferenceAssemblies referenceAssemblies = ReferenceAssemblyCatalog.Catalog[referenceAssemblyGroup]; + await new Test { TestCode = source, FixedCode = source, + ReferenceAssemblies = referenceAssemblies, }.RunAsync().ConfigureAwait(false); } } diff --git a/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs b/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs index ce2c93c2c..f6571e02a 100644 --- a/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs +++ b/Source/Moq.Analyzers.Test/Helpers/CodeFixVerifier.cs @@ -1,5 +1,6 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; namespace Moq.Analyzers.Test.Helpers; @@ -7,12 +8,15 @@ internal static class CodeFixVerifier where TAnalyzer : DiagnosticAnalyzer, new() where TCodeFixProvider : CodeFixProvider, new() { - public static async Task VerifyCodeFixAsync(string originalSource, string fixedSource) + public static async Task VerifyCodeFixAsync(string originalSource, string fixedSource, string referenceAssemblyGroup) { + ReferenceAssemblies referenceAssemblies = ReferenceAssemblyCatalog.Catalog[referenceAssemblyGroup]; + await new Test { TestCode = originalSource, FixedCode = fixedSource, + ReferenceAssemblies = referenceAssemblies, }.RunAsync().ConfigureAwait(false); } } diff --git a/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs b/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs index 75e26542f..d815efd2e 100644 --- a/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs +++ b/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs @@ -8,13 +8,19 @@ namespace Moq.Analyzers.Test.Helpers; /// package resolution only happens once for a given configuration. /// /// -/// This class is currently very simple and assumes that the only package that will be resolved is Moq for .NET 8.0. As our testing needs -/// get more complicated, we can either manage the combinations ourselves -/// (as done in https://github.com/dotnet/roslyn-analyzers/blob/4d5fd9da36d64d4c3370b8813122e226844fc6ed/src/Test.Utilities/AdditionalMetadataReferences.cs) -/// or consider filing an issue in https://github.com/dotnet/roslyn-sdk to clarify best practices. +/// It would be more straightforward to pass around ReferenceAssemblies instances directly, but using non-primitive types causes +/// Visual Studio's Test Explorer to collapse all test cases down to a single entry, which makes it harder to see which test cases +/// are failing or debug a single test case. /// internal static class ReferenceAssemblyCatalog { - // TODO: We should also be testing a newer version of Moq. See https://github.com/rjmurillo/moq.analyzers/issues/58. - public static ReferenceAssemblies Net80WithOldMoq { get; } = ReferenceAssemblies.Net.Net80.AddPackages([new PackageIdentity("Moq", "4.8.2")]); + public static string Net80WithOldMoq => nameof(Net80WithOldMoq); + + public static string Net80WithNewMoq => nameof(Net80WithNewMoq); + + public static IReadOnlyDictionary Catalog { get; } = new Dictionary(StringComparer.Ordinal) + { + { nameof(Net80WithOldMoq), ReferenceAssemblies.Net.Net80.AddPackages([new PackageIdentity("Moq", "4.8.2")]) }, + { nameof(Net80WithNewMoq), ReferenceAssemblies.Net.Net80.AddPackages([new PackageIdentity("Moq", "4.18.4")]) }, + }; } diff --git a/Source/Moq.Analyzers.Test/Helpers/Test.cs b/Source/Moq.Analyzers.Test/Helpers/Test.cs index 01e2093bb..b2a64f769 100644 --- a/Source/Moq.Analyzers.Test/Helpers/Test.cs +++ b/Source/Moq.Analyzers.Test/Helpers/Test.cs @@ -28,6 +28,5 @@ public Test() TestState.Sources.Add(globalUsings); FixedState.Sources.Add(globalUsings); - ReferenceAssemblies = ReferenceAssemblyCatalog.Net80WithOldMoq; } } diff --git a/Source/Moq.Analyzers.Test/Helpers/TestDataExtensions.cs b/Source/Moq.Analyzers.Test/Helpers/TestDataExtensions.cs new file mode 100644 index 000000000..a8c67deb2 --- /dev/null +++ b/Source/Moq.Analyzers.Test/Helpers/TestDataExtensions.cs @@ -0,0 +1,22 @@ +namespace Moq.Analyzers.Test.Helpers; + +internal static class TestDataExtensions +{ + public static IEnumerable WithNamespaces(this IEnumerable data) + { + foreach (object[] item in data) + { + yield return item.Prepend(string.Empty).ToArray(); + yield return item.Prepend("namespace MyNamespace;").ToArray(); + } + } + + public static IEnumerable WithReferenceAssemblyGroups(this IEnumerable data) + { + foreach (object[] item in data) + { + yield return item.Prepend(ReferenceAssemblyCatalog.Net80WithOldMoq).ToArray(); + yield return item.Prepend(ReferenceAssemblyCatalog.Net80WithNewMoq).ToArray(); + } + } +} diff --git a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs index 01ab2cead..909260343 100644 --- a/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/NoConstructorArgumentsForInterfaceMockAnalyzerTests.cs @@ -6,23 +6,23 @@ public class NoConstructorArgumentsForInterfaceMockAnalyzerTests { public static IEnumerable InterfaceMockingTestData() { - foreach (string @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { - yield return [@namespace, """new Mock{|Moq1001:(25, true)|};"""]; - yield return [@namespace, """new Mock{|Moq1001:("123")|};"""]; - yield return [@namespace, """new Mock{|Moq1001:(MockBehavior.Default, "123")|};"""]; - yield return [@namespace, """new Mock{|Moq1001:(MockBehavior.Strict, 25, true)|};"""]; - yield return [@namespace, """new Mock{|Moq1001:(MockBehavior.Loose, 25, true)|};"""]; - yield return [@namespace, """new Mock();"""]; - yield return [@namespace, """new Mock(MockBehavior.Default);"""]; - yield return [@namespace, """new Mock(MockBehavior.Strict);"""]; - yield return [@namespace, """new Mock(MockBehavior.Loose);"""]; - } + ["""new Mock{|Moq1001:(25, true)|};"""], + ["""new Mock{|Moq1001:("123")|};"""], + ["""new Mock{|Moq1001:(MockBehavior.Default, "123")|};"""], + ["""new Mock{|Moq1001:(MockBehavior.Strict, 25, true)|};"""], + ["""new Mock{|Moq1001:(MockBehavior.Loose, 25, true)|};"""], + ["""new Mock();"""], + ["""new Mock(MockBehavior.Default);"""], + ["""new Mock(MockBehavior.Strict);"""], + ["""new Mock(MockBehavior.Loose);"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(InterfaceMockingTestData))] - public async Task ShouldAnalyzeInterfaceConstructors(string @namespace, string mock) + public async Task ShouldAnalyzeInterfaceConstructors(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -40,7 +40,8 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } // TODO: This feels like it should be in every analyzer's tests. Tracked by #75. @@ -87,7 +88,8 @@ private void TestFakeMoq() var mock6 = new Mock(MockBehavior.Loose); } } - """); + """, + ReferenceAssemblyCatalog.Net80WithNewMoq); } // TODO: This feels like it should be in every analyzer's tests. Tracked by #75. @@ -134,7 +136,8 @@ private void TestRealMoqWithBadParameters() var mock6 = new Moq.Mock{|Moq1001:(MockBehavior.Default)|}; } } - """); + """, + ReferenceAssemblyCatalog.Net80WithNewMoq); } // TODO: This feels like it should be in every analyzer's tests. Tracked by #75. @@ -177,6 +180,7 @@ private void TestRealMoqWithGoodParameters() var mock2 = new Moq.Mock(Moq.MockBehavior.Default); } } - """); + """, + ReferenceAssemblyCatalog.Net80WithNewMoq); } } diff --git a/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs b/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs index 707d66a5d..2bb96bc1d 100644 --- a/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/NoMethodsInPropertySetupAnalyzerTests.cs @@ -6,21 +6,21 @@ public class NoMethodsInPropertySetupAnalyzerTests { public static IEnumerable TestData() { - foreach (string @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { - yield return [@namespace, """new Mock().SetupGet(x => x.Prop1);"""]; - yield return [@namespace, """new Mock().SetupGet(x => x.Prop2);"""]; - yield return [@namespace, """new Mock().SetupSet(x => x.Prop1 = "1");"""]; - yield return [@namespace, """new Mock().SetupSet(x => x.Prop3 = "2");"""]; - yield return [@namespace, """new Mock().Setup(x => x.Method());"""]; - yield return [@namespace, """new Mock().SetupGet(x => {|Moq1101:x.Method()|});"""]; - yield return [@namespace, """new Mock().SetupSet(x => {|Moq1101:x.Method()|});"""]; - } + ["""new Mock().SetupGet(x => x.Prop1);"""], + ["""new Mock().SetupGet(x => x.Prop2);"""], + ["""new Mock().SetupSet(x => x.Prop1 = "1");"""], + ["""new Mock().SetupSet(x => x.Prop3 = "2");"""], + ["""new Mock().Setup(x => x.Method());"""], + ["""new Mock().SetupGet(x => {|Moq1101:x.Method()|});"""], + ["""new Mock().SetupSet(x => {|Moq1101:x.Method()|});"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShouldAnalyzePropertySetup(string @namespace, string mock) + public async Task ShouldAnalyzePropertySetup(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -44,6 +44,7 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } } diff --git a/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs b/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs index 14d4b9c06..f8d9eabfe 100644 --- a/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/NoSealedClassMocksAnalyzerTests.cs @@ -6,16 +6,16 @@ public class NoSealedClassMocksAnalyzerTests { public static IEnumerable TestData() { - foreach (string @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { - yield return [@namespace, """new Mock<{|Moq1000:FooSealed|}>();"""]; - yield return [@namespace, """new Mock();"""]; - } + ["""new Mock<{|Moq1000:FooSealed|}>();"""], + ["""new Mock();"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShoulAnalyzeSealedClassMocks(string @namespace, string mock) + public async Task ShoulAnalyzeSealedClassMocks(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -32,6 +32,7 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } } diff --git a/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs b/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs index 8d78856f8..a6a0488af 100644 --- a/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests.cs @@ -6,22 +6,22 @@ public class SetupShouldBeUsedOnlyForOverridableMembersAnalyzerTests { public static IEnumerable TestData() { - foreach (string @namespace in new[] { string.Empty, "namespace MyNamespace;" }) + return new object[][] { - yield return [@namespace, """new Mock().Setup(x => {|Moq1200:x.Calculate()|});"""]; - yield return [@namespace, """new Mock().Setup(x => {|Moq1200:x.Property|});"""]; - yield return [@namespace, """new Mock().Setup(x => {|Moq1200:x.Calculate(It.IsAny(), It.IsAny(), It.IsAny())|});"""]; - yield return [@namespace, """new Mock().Setup(x => x.Calculate(It.IsAny(), It.IsAny()));"""]; - yield return [@namespace, """new Mock().Setup(x => x.Calculate(It.IsAny(), It.IsAny()));"""]; - yield return [@namespace, """new Mock().Setup(x => x.TestProperty);"""]; - yield return [@namespace, """new Mock().Setup(x => x.Calculate(It.IsAny(), It.IsAny()));"""]; - yield return [@namespace, """new Mock().Setup(x => x.DoSth());"""]; - } + ["""new Mock().Setup(x => {|Moq1200:x.Calculate()|});"""], + ["""new Mock().Setup(x => {|Moq1200:x.Property|});"""], + ["""new Mock().Setup(x => {|Moq1200:x.Calculate(It.IsAny(), It.IsAny(), It.IsAny())|});"""], + ["""new Mock().Setup(x => x.Calculate(It.IsAny(), It.IsAny()));"""], + ["""new Mock().Setup(x => x.Calculate(It.IsAny(), It.IsAny()));"""], + ["""new Mock().Setup(x => x.TestProperty);"""], + ["""new Mock().Setup(x => x.Calculate(It.IsAny(), It.IsAny()));"""], + ["""new Mock().Setup(x => x.DoSth());"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShouldAnalyzeSetupForOverridableMembers(string @namespace, string mock) + public async Task ShouldAnalyzeSetupForOverridableMembers(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -55,6 +55,7 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } } diff --git a/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs b/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs index 36822dc46..60b191d11 100644 --- a/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs +++ b/Source/Moq.Analyzers.Test/SetupShouldNotIncludeAsyncResultAnalyzerTests.cs @@ -6,17 +6,17 @@ public class SetupShouldNotIncludeAsyncResultAnalyzerTests { public static IEnumerable TestData() { - foreach (string @namespace in new[] { string.Empty, "namespace MyNamespace; "}) + return new object[][] { - yield return [@namespace, """new Mock().Setup(c => c.TaskAsync());"""]; - yield return [@namespace, """new Mock().Setup(c => c.GenericTaskAsync()).ReturnsAsync(string.Empty);"""]; - yield return [@namespace, """new Mock().Setup(c => {|Moq1201:c.GenericTaskAsync().Result|});"""]; - } + ["""new Mock().Setup(c => c.TaskAsync());"""], + ["""new Mock().Setup(c => c.GenericTaskAsync()).ReturnsAsync(string.Empty);"""], + ["""new Mock().Setup(c => {|Moq1201:c.GenericTaskAsync().Result|});"""], + }.WithNamespaces().WithReferenceAssemblyGroups(); } [Theory] [MemberData(nameof(TestData))] - public async Task ShouldAnalyzeSetupForAsyncResult(string @namespace, string mock) + public async Task ShouldAnalyzeSetupForAsyncResult(string referenceAssemblyGroup, string @namespace, string mock) { await Verifier.VerifyAnalyzerAsync( $$""" @@ -36,6 +36,7 @@ private void Test() {{mock}} } } - """); + """, + referenceAssemblyGroup); } } From 8fbb589223502eae1b0564c66bcc0e3a292fdc33 Mon Sep 17 00:00:00 2001 From: Matt Kotsenas Date: Wed, 12 Jun 2024 00:25:24 -0700 Subject: [PATCH 2/2] Add comment to document why Moq test versions were picked --- Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs b/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs index d815efd2e..383be5fbb 100644 --- a/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs +++ b/Source/Moq.Analyzers.Test/Helpers/ReferenceAssemblyCatalog.cs @@ -20,7 +20,11 @@ internal static class ReferenceAssemblyCatalog public static IReadOnlyDictionary Catalog { get; } = new Dictionary(StringComparer.Ordinal) { + // 4.8.2 was one of the first popular versions of Moq. Ensure this version is prior to 4.13.1, as it changed the internal + // implementation of `.As()` (see https://github.com/devlooped/moq/commit/b552aeddd82090ee0f4743a1ab70a16f3e6d2d11). { nameof(Net80WithOldMoq), ReferenceAssemblies.Net.Net80.AddPackages([new PackageIdentity("Moq", "4.8.2")]) }, + + // 4.18.4 is currently the most downloaded version of Moq. { nameof(Net80WithNewMoq), ReferenceAssemblies.Net.Net80.AddPackages([new PackageIdentity("Moq", "4.18.4")]) }, }; }