diff --git a/tests/Moq.Analyzers.Test/SetExplicitMockBehaviorCodeFixTests.cs b/tests/Moq.Analyzers.Test/SetExplicitMockBehaviorCodeFixTests.cs index 97ab8e121..f9c54e249 100644 --- a/tests/Moq.Analyzers.Test/SetExplicitMockBehaviorCodeFixTests.cs +++ b/tests/Moq.Analyzers.Test/SetExplicitMockBehaviorCodeFixTests.cs @@ -33,6 +33,26 @@ public static IEnumerable TestData() ], }.WithNamespaces().WithMoqReferenceAssemblyGroups(); + IEnumerable mockConstructorsWithTargetTypedNew = new object[][] + { + [ + """Mock mock = {|Moq1400:new()|};""", + """Mock mock = new(MockBehavior.Loose);""", + ], + [ + """Mock mock = {|Moq1400:new(MockBehavior.Default)|};""", + """Mock mock = new(MockBehavior.Loose);""", + ], + [ + """Mock mock = new(MockBehavior.Loose);""", + """Mock mock = new(MockBehavior.Loose);""", + ], + [ + """Mock mock = new(MockBehavior.Strict);""", + """Mock mock = new(MockBehavior.Strict);""", + ], + }.WithNamespaces().WithMoqReferenceAssemblyGroups(); + IEnumerable mockConstructorsWithExpressions = new object[][] { [ @@ -89,7 +109,7 @@ public static IEnumerable TestData() ], }.WithNamespaces().WithNewMoqReferenceAssemblyGroups(); - return mockConstructors.Union(mockConstructorsWithExpressions).Union(fluentBuilders).Union(mockRepositories); + return mockConstructors.Union(mockConstructorsWithTargetTypedNew).Union(mockConstructorsWithExpressions).Union(fluentBuilders).Union(mockRepositories); } [Theory] @@ -139,3 +159,644 @@ private void Test() // via the public analyzer/codefix APIs or test harness. These paths are not testable without // breaking encapsulation or using unsupported reflection/mocking of Roslyn internals. } + + [Fact] + public async Task ShouldHandleNestedMockConstructors() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var outer = {|Moq1400:new Mock()|}; + var inner = {|Moq1400:new Mock()|}; + } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var outer = new Mock(MockBehavior.Loose); + var inner = new Mock(MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockConstructorsInFieldDeclarations() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private readonly Mock _mock = {|Moq1400:new Mock()|}; + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private readonly Mock _mock = new Mock(MockBehavior.Loose); + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockConstructorsInPropertyDeclarations() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + public Mock MockProperty { get; } = {|Moq1400:new Mock()|}; + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + public Mock MockProperty { get; } = new Mock(MockBehavior.Loose); + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMultipleMockConstructorsInSameMethod() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + public interface IOther + { + string Process(string input); + } + + internal class UnitTest + { + private void Test() + { + var mock1 = {|Moq1400:new Mock()|}; + var mock2 = {|Moq1400:new Mock(MockBehavior.Default)|}; + var mock3 = new Mock(MockBehavior.Strict); + } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + public interface IOther + { + string Process(string input); + } + + internal class UnitTest + { + private void Test() + { + var mock1 = new Mock(MockBehavior.Loose); + var mock2 = new Mock(MockBehavior.Loose); + var mock3 = new Mock(MockBehavior.Strict); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockConstructorsInMethodParameters() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + ProcessMock({|Moq1400:new Mock()|}); + } + + private void ProcessMock(Mock mock) { } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + ProcessMock(new Mock(MockBehavior.Loose)); + } + + private void ProcessMock(Mock mock) { } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockConstructorsInReturnStatements() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private Mock CreateMock() + { + return {|Moq1400:new Mock()|}; + } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private Mock CreateMock() + { + return new Mock(MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleGenericMockConstructors() + { + const string original = """ + using Moq; + using System.Collections.Generic; + + public interface IGenericSample + { + T Process(T input); + } + + internal class UnitTest + { + private void Test() + { + var stringMock = {|Moq1400:new Mock>()|}; + var intMock = {|Moq1400:new Mock>(MockBehavior.Default)|}; + var listMock = {|Moq1400:new Mock>>()|}; + } + } + """; + + const string quickFix = """ + using Moq; + using System.Collections.Generic; + + public interface IGenericSample + { + T Process(T input); + } + + internal class UnitTest + { + private void Test() + { + var stringMock = new Mock>(MockBehavior.Loose); + var intMock = new Mock>(MockBehavior.Loose); + var listMock = new Mock>>(MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockConstructorsInConditionalExpressions() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test(bool condition) + { + var mock = condition ? {|Moq1400:new Mock()|} : {|Moq1400:new Mock(MockBehavior.Default)|}; + } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test(bool condition) + { + var mock = condition ? new Mock(MockBehavior.Loose) : new Mock(MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockConstructorsInArrayInitializers() + { + const string original = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var mocks = new Mock[] + { + {|Moq1400:new Mock()|}, + {|Moq1400:new Mock(MockBehavior.Default)|}, + new Mock(MockBehavior.Strict) + }; + } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var mocks = new Mock[] + { + new Mock(MockBehavior.Loose), + new Mock(MockBehavior.Loose), + new Mock(MockBehavior.Strict) + }; + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockOfWithComplexExpressions() + { + const string original = """ + using Moq; + using System; + + public interface ISample + { + int Calculate(int a, int b); + string Name { get; set; } + } + + internal class UnitTest + { + private void Test() + { + var mock = {|Moq1400:Mock.Of(x => x.Name == "Test" && x.Calculate(1, 2) == 3)|}; + } + } + """; + + const string quickFix = """ + using Moq; + using System; + + public interface ISample + { + int Calculate(int a, int b); + string Name { get; set; } + } + + internal class UnitTest + { + private void Test() + { + var mock = Mock.Of(x => x.Name == "Test" && x.Calculate(1, 2) == 3, MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMockRepositoryWithCallbackArguments() + { + const string original = """ + using Moq; + using System; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var repository = {|Moq1400:new MockRepository(MockBehavior.Default)|}; + repository.CallbackVerification += (sender, args) => Console.WriteLine("Callback"); + } + } + """; + + const string quickFix = """ + using Moq; + using System; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var repository = new MockRepository(MockBehavior.Loose); + repository.CallbackVerification += (sender, args) => Console.WriteLine("Callback"); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Theory] + [InlineData("MockBehavior.Default")] + [InlineData("")] + public async Task ShouldHandleVariousDefaultBehaviorScenarios(string behaviorParam) + { + string mockCall = string.IsNullOrEmpty(behaviorParam) ? + "{|Moq1400:new Mock()|}" : + $"{{|Moq1400:new Mock({behaviorParam})|}}"; + + string original = $$""" + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var mock = {{mockCall}}; + } + } + """; + + const string quickFix = """ + using Moq; + + public interface ISample + { + int Calculate(int a, int b); + } + + internal class UnitTest + { + private void Test() + { + var mock = new Mock(MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleComplexInheritanceScenarios() + { + const string original = """ + using Moq; + + public interface IBaseInterface + { + void BaseMethod(); + } + + public interface IDerivedInterface : IBaseInterface + { + void DerivedMethod(); + } + + public abstract class BaseClass + { + public abstract void AbstractMethod(); + } + + public class DerivedClass : BaseClass + { + public override void AbstractMethod() { } + } + + internal class UnitTest + { + private void Test() + { + var interfaceMock = {|Moq1400:new Mock()|}; + var abstractMock = {|Moq1400:new Mock()|}; + var concreteMock = {|Moq1400:new Mock()|}; + } + } + """; + + const string quickFix = """ + using Moq; + + public interface IBaseInterface + { + void BaseMethod(); + } + + public interface IDerivedInterface : IBaseInterface + { + void DerivedMethod(); + } + + public abstract class BaseClass + { + public abstract void AbstractMethod(); + } + + public class DerivedClass : BaseClass + { + public override void AbstractMethod() { } + } + + internal class UnitTest + { + private void Test() + { + var interfaceMock = new Mock(MockBehavior.Loose); + var abstractMock = new Mock(MockBehavior.Loose); + var concreteMock = new Mock(MockBehavior.Loose); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } + + [Fact] + public async Task ShouldHandleMixedConstructorParametersAndBehavior() + { + const string original = """ + using Moq; + + public class Calculator + { + private readonly string _name; + public Calculator(string name) { _name = name; } + public int Calculate(int a, int b) { return a + b; } + } + + internal class UnitTest + { + private void Test() + { + var mock1 = {|Moq1400:new Mock("test")|}; + var mock2 = {|Moq1400:new Mock("test", MockBehavior.Default)|}; + var mock3 = new Mock("test", MockBehavior.Strict); + } + } + """; + + const string quickFix = """ + using Moq; + + public class Calculator + { + private readonly string _name; + public Calculator(string name) { _name = name; } + public int Calculate(int a, int b) { return a + b; } + } + + internal class UnitTest + { + private void Test() + { + var mock1 = new Mock(MockBehavior.Loose, "test"); + var mock2 = new Mock(MockBehavior.Loose, "test"); + var mock3 = new Mock("test", MockBehavior.Strict); + } + } + """; + + await Verifier.VerifyCodeFixAsync(original, quickFix); + } +}