From ba9bac1f25d2b539ff1b516d7a09fae61b6eb41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Valentin=20Breu=C3=9F?= Date: Fri, 15 May 2026 07:23:02 +0200 Subject: [PATCH] coverage: improve code coverage --- .../MoqAnalyzerTests.cs | 66 +++++++++++++++++ .../MoqCodeFixProviderTests.NewMockTests.cs | 25 +++++++ .../MoqCodeFixProviderTests.RaiseTests.cs | 72 ++++++++++++++++++ .../MoqCodeFixProviderTests.SetupTests.cs | 70 ++++++++++++++++++ .../MoqCodeFixProviderTests.VerifyTests.cs | 70 ++++++++++++++++++ .../NSubstituteAnalyzerTests.cs | 18 +++++ ...ituteCodeFixProviderTests.CreationTests.cs | 50 +++++++++++++ ...stituteCodeFixProviderTests.NestedTests.cs | 36 +++++++++ ...bstituteCodeFixProviderTests.SetupTests.cs | 74 +++++++++++++++++++ ...stituteCodeFixProviderTests.WhenDoTests.cs | 36 +++++++++ 10 files changed, 517 insertions(+) diff --git a/Tests/Mockolate.Migration.Tests/MoqAnalyzerTests.cs b/Tests/Mockolate.Migration.Tests/MoqAnalyzerTests.cs index d3f9cd1..a95a8d6 100644 --- a/Tests/Mockolate.Migration.Tests/MoqAnalyzerTests.cs +++ b/Tests/Mockolate.Migration.Tests/MoqAnalyzerTests.cs @@ -5,6 +5,57 @@ namespace Mockolate.Migration.Tests; public class MoqAnalyzerTests { + [Fact] + public async Task MoqFactoryMethodEndingInMock_AsArgument_IsNotFlagged() + => await Verifier.VerifyAnalyzerAsync(""" + using Moq; + + public interface IFoo { } + + namespace Moq.TestHelpers + { + public static class Factory + { + public static T MyMock() where T : class => null!; + } + } + + public class Tests + { + public void Consume(IFoo foo) { } + public void Test() + { + Consume(Moq.TestHelpers.Factory.MyMock()); + } + } + """); + + [Fact] + public async Task MoqFactoryMethodEndingInMock_ChainedCall_IsFlagged() + => await Verifier.VerifyAnalyzerAsync(""" + using Moq; + + public interface IFoo { int Bar(); } + + namespace Moq.TestHelpers + { + public static class Factory + { + public static T MyMock() where T : class => null!; + } + } + + public class Tests + { + public void Test() + { + var value = {|#0:Moq.TestHelpers.Factory.MyMock().Bar()|}; + } + } + """, + Verifier.Diagnostic(Rules.MoqRule) + .WithLocation(0)); + [Fact] public async Task NewMockExplicit_IsFlagged() => await Verifier.VerifyAnalyzerAsync(""" @@ -40,4 +91,19 @@ public void Test() """, Verifier.Diagnostic(Rules.MoqRule) .WithLocation(0)); + + [Fact] + public async Task NonMoqInvocation_IsNotFlagged() + => await Verifier.VerifyAnalyzerAsync(""" + public interface IFoo { int Bar(); } + + public class Tests + { + public IFoo CreateMock() => null!; + public void Test() + { + var value = CreateMock().Bar(); + } + } + """); } diff --git a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.NewMockTests.cs b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.NewMockTests.cs index 466fa86..de24452 100644 --- a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.NewMockTests.cs +++ b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.NewMockTests.cs @@ -7,6 +7,31 @@ public partial class MoqCodeFixProviderTests { public sealed class NewMockTests { + [Fact] + public async Task AsPropertyInitializer_IsReplaced() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + + public interface IFoo { } + + public class Tests + { + public Mock MockProperty { get; } = [|new Mock()|]; + } + """, + """ + using Moq; + using Mockolate; + + public interface IFoo { } + + public class Tests + { + public IFoo MockProperty { get; } = IFoo.CreateMock(); + } + """); + [Fact] public async Task IsReplaced() => await Verifier.VerifyCodeFixAsync( diff --git a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.RaiseTests.cs b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.RaiseTests.cs index ae28121..6913a6b 100644 --- a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.RaiseTests.cs +++ b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.RaiseTests.cs @@ -7,6 +7,43 @@ public partial class MoqCodeFixProviderTests { public sealed class RaiseTests { + [Fact] + public async Task WithDerivedEventArgs_PrependsNullSender() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + using System; + + public class MyEventArgs : EventArgs { public int Value; } + public interface IFoo { event EventHandler MyEvent; } + + public class Tests + { + public void Test() + { + var mock = [|new Mock()|]; + mock.Raise(m => m.MyEvent += null, new MyEventArgs()); + } + } + """, + """ + using Moq; + using System; + using Mockolate; + + public class MyEventArgs : EventArgs { public int Value; } + public interface IFoo { event EventHandler MyEvent; } + + public class Tests + { + public void Test() + { + var mock = IFoo.CreateMock(); + mock.Mock.Raise.MyEvent(null, new MyEventArgs()); + } + } + """); + [Fact] public async Task WithEventArgs_PrependsNullSender() => await Verifier.VerifyCodeFixAsync( @@ -76,5 +113,40 @@ public void Test() } } """); + + [Fact] + public async Task WithSingleNonEventArgsValue_DoesNotPrependSender() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + using System; + + public interface IFoo { event Action MyEvent; } + + public class Tests + { + public void Test() + { + var mock = [|new Mock()|]; + mock.Raise(m => m.MyEvent += null, 42); + } + } + """, + """ + using Moq; + using System; + using Mockolate; + + public interface IFoo { event Action MyEvent; } + + public class Tests + { + public void Test() + { + var mock = IFoo.CreateMock(); + mock.Mock.Raise.MyEvent(42); + } + } + """); } } diff --git a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.SetupTests.cs b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.SetupTests.cs index c3d993b..c32dea1 100644 --- a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.SetupTests.cs +++ b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.SetupTests.cs @@ -563,6 +563,76 @@ public void Test() } """); + [Fact] + public async Task SetupProperty_Nested_MigratesInitializeWithViaNavigationChain() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + + public class Bar { public virtual string Name { get; set; } = ""; } + public interface IFoo { Bar Bar { get; set; } } + + public class Tests + { + public void Test() + { + var mock = [|new Mock()|]; + mock.SetupProperty(foo => foo.Bar.Name, "value"); + } + } + """, + """ + using Moq; + using Mockolate; + + public class Bar { public virtual string Name { get; set; } = ""; } + public interface IFoo { Bar Bar { get; set; } } + + public class Tests + { + public void Test() + { + var mock = IFoo.CreateMock(); + mock.Bar.Mock.Setup.Name.InitializeWith("value"); + } + } + """); + + [Fact] + public async Task SetupProperty_NestedWithoutDefault_MigratesRegisterViaNavigationChain() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + + public class Bar { public virtual string Name { get; set; } = ""; } + public interface IFoo { Bar Bar { get; set; } } + + public class Tests + { + public void Test() + { + var mock = [|new Mock()|]; + mock.SetupProperty(foo => foo.Bar.Name); + } + } + """, + """ + using Moq; + using Mockolate; + + public class Bar { public virtual string Name { get; set; } = ""; } + public interface IFoo { Bar Bar { get; set; } } + + public class Tests + { + public void Test() + { + var mock = IFoo.CreateMock(); + mock.Bar.Mock.Setup.Name.Register(); + } + } + """); + [Fact] public async Task SetupProperty_WithDefault_MigratesInitializeWith() => await Verifier.VerifyCodeFixAsync( diff --git a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.VerifyTests.cs b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.VerifyTests.cs index 231e91a..c73b21a 100644 --- a/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.VerifyTests.cs +++ b/Tests/Mockolate.Migration.Tests/MoqCodeFixProviderTests.VerifyTests.cs @@ -41,6 +41,42 @@ public void Test() } """); + [Fact] + public async Task NestedMethod_MigratesViaNavigationChain() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + + public interface IBar { int Compute(int x); } + public interface IFoo { IBar Child { get; } } + + public class Tests + { + public void Test() + { + var mock = [|new Mock()|]; + mock.Verify(m => m.Child.Compute(It.IsAny()), Times.Once()); + } + } + """, + """ + using Moq; + using Mockolate; + using Mockolate.Verify; + + public interface IBar { int Compute(int x); } + public interface IFoo { IBar Child { get; } } + + public class Tests + { + public void Test() + { + var mock = IFoo.CreateMock(); + mock.Child.Mock.Verify.Compute(It.IsAny()).Once(); + } + } + """); + [Fact] public async Task WithNoTimes_MigratesVerifyToAtLeastOnce() => await Verifier.VerifyCodeFixAsync( @@ -109,6 +145,40 @@ public void Test() } """); + [Fact] + public async Task WithTimesBetweenExclusive_NonLiteralBounds_AdjustsBoundsUsingAddSubtract() + => await Verifier.VerifyCodeFixAsync( + """ + using Moq; + + public interface IFoo { bool Bar(string x); } + + public class Tests + { + public void Test(int low, int high) + { + var mock = [|new Mock()|]; + mock.Verify(m => m.Bar(It.IsAny()), Times.Between(low, high, Range.Exclusive)); + } + } + """, + """ + using Moq; + using Mockolate; + using Mockolate.Verify; + + public interface IFoo { bool Bar(string x); } + + public class Tests + { + public void Test(int low, int high) + { + var mock = IFoo.CreateMock(); + mock.Mock.Verify.Bar(It.IsAny()).Between(low + 1, high - 1); + } + } + """); + [Theory] [InlineData("Times.Never", ".Never()")] [InlineData("Times.Never()", ".Never()")] diff --git a/Tests/Mockolate.Migration.Tests/NSubstituteAnalyzerTests.cs b/Tests/Mockolate.Migration.Tests/NSubstituteAnalyzerTests.cs index 83553df..b427d17 100644 --- a/Tests/Mockolate.Migration.Tests/NSubstituteAnalyzerTests.cs +++ b/Tests/Mockolate.Migration.Tests/NSubstituteAnalyzerTests.cs @@ -24,6 +24,24 @@ public void Test() Verifier.Diagnostic(Rules.NSubstituteRule) .WithLocation(0)); + [Fact] + public async Task SubstituteFor_ChainedMethodCall_FlagsOuterExpression() + => await Verifier.VerifyAnalyzerAsync(""" + using NSubstitute; + + public interface IFoo { int Bar(); } + + public class Tests + { + public void Test() + { + var value = {|#0:Substitute.For().Bar()|}; + } + } + """, + Verifier.Diagnostic(Rules.NSubstituteRule) + .WithLocation(0)); + [Fact] public async Task SubstituteFor_IsFlagged() => await Verifier.VerifyAnalyzerAsync(""" diff --git a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.CreationTests.cs b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.CreationTests.cs index 4ba392f..0a64343 100644 --- a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.CreationTests.cs +++ b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.CreationTests.cs @@ -7,6 +7,56 @@ public partial class NSubstituteCodeFixProviderTests { public sealed class CreationTests { + [Fact] + public async Task SubstituteFor_AsExpressionBodiedReturn_IsReplaced() + => await Verifier.VerifyCodeFixAsync( + """ + using NSubstitute; + + public interface IFoo { } + + public class Tests + { + public IFoo CreateSub() => [|Substitute.For()|]; + } + """, + """ + using NSubstitute; + using Mockolate; + + public interface IFoo { } + + public class Tests + { + public IFoo CreateSub() => IFoo.CreateMock(); + } + """); + + [Fact] + public async Task SubstituteFor_AsPropertyInitializer_IsReplaced() + => await Verifier.VerifyCodeFixAsync( + """ + using NSubstitute; + + public interface IFoo { } + + public class Tests + { + public IFoo SubProperty { get; } = [|Substitute.For()|]; + } + """, + """ + using NSubstitute; + using Mockolate; + + public interface IFoo { } + + public class Tests + { + public IFoo SubProperty { get; } = IFoo.CreateMock(); + } + """); + [Fact] public async Task SubstituteFor_FullyQualified_IsReplaced() => await Verifier.VerifyCodeFixAsync( diff --git a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.NestedTests.cs b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.NestedTests.cs index dd6037c..9a8d222 100644 --- a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.NestedTests.cs +++ b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.NestedTests.cs @@ -78,5 +78,41 @@ public void Test() } } """); + + [Fact] + public async Task NestedReceivedMethod_RewritesToVerifyOnNestedMock() + => await Verifier.VerifyCodeFixAsync( + """ + using NSubstitute; + + public interface IBar { int Compute(int x); } + public interface IFoo { IBar Child { get; } } + + public class Tests + { + public void Test() + { + var sub = [|Substitute.For()|]; + sub.Child.Received(2).Compute(1); + } + } + """, + """ + using NSubstitute; + using Mockolate; + using Mockolate.Verify; + + public interface IBar { int Compute(int x); } + public interface IFoo { IBar Child { get; } } + + public class Tests + { + public void Test() + { + var sub = IFoo.CreateMock(); + sub.Child.Mock.Verify.Compute(1).Exactly(2); + } + } + """); } } diff --git a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.SetupTests.cs b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.SetupTests.cs index 75b5986..c95e6f9 100644 --- a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.SetupTests.cs +++ b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.SetupTests.cs @@ -506,6 +506,41 @@ public void Test() } """); + [Fact] + public async Task ReturnsNull_PreservesConfiguratorName() + => await Verifier.VerifyCodeFixAsync( + """ + using NSubstitute; + using NSubstitute.ReturnsExtensions; + + public interface IFoo { string Bar(int x); } + + public class Tests + { + public void Test() + { + var sub = [|Substitute.For()|]; + sub.Bar(1).ReturnsNull(); + } + } + """, + """ + using NSubstitute; + using NSubstitute.ReturnsExtensions; + using Mockolate; + + public interface IFoo { string Bar(int x); } + + public class Tests + { + public void Test() + { + var sub = IFoo.CreateMock(); + sub.Mock.Setup.Bar(1).ReturnsNull(); + } + } + """); + [Fact] public async Task SequentialPropertyReturns_AreSplitIntoChain() => await Verifier.VerifyCodeFixAsync( @@ -572,6 +607,45 @@ public void Test() } """); + [Fact] + public async Task ThrowsAsync_PreservesConfiguratorName() + => await Verifier.VerifyCodeFixAsync( + """ + using System; + using System.Threading.Tasks; + using NSubstitute; + using NSubstitute.ExceptionExtensions; + + public interface IFoo { Task BarAsync(int x); } + + public class Tests + { + public void Test() + { + var sub = [|Substitute.For()|]; + sub.BarAsync(1).ThrowsAsync(new InvalidOperationException()); + } + } + """, + """ + using System; + using System.Threading.Tasks; + using NSubstitute; + using NSubstitute.ExceptionExtensions; + using Mockolate; + + public interface IFoo { Task BarAsync(int x); } + + public class Tests + { + public void Test() + { + var sub = IFoo.CreateMock(); + sub.Mock.Setup.BarAsync(1).ThrowsAsync(new InvalidOperationException()); + } + } + """); + [Fact] public async Task ThrowsForAnyArgsGeneric_AddsAnyParametersAndRenamesToThrows() => await Verifier.VerifyCodeFixAsync( diff --git a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.WhenDoTests.cs b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.WhenDoTests.cs index fd06426..9e10d0e 100644 --- a/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.WhenDoTests.cs +++ b/Tests/Mockolate.Migration.Tests/NSubstituteCodeFixProviderTests.WhenDoTests.cs @@ -75,6 +75,42 @@ public void Test() } """); + [Fact] + public async Task WhenMethod_DoWithBareCallInfoReference_AddsTodoComment() + => await Verifier.VerifyCodeFixAsync( + """ + using System; + using NSubstitute; + + public interface IFoo { void Bar(int x); } + + public class Tests + { + public void Test() + { + var sub = [|Substitute.For()|]; + sub.When(x => x.Bar(1)).Do(call => Console.WriteLine(call)); + } + } + """, + """ + using System; + using NSubstitute; + using Mockolate; + + public interface IFoo { void Bar(int x); } + + public class Tests + { + public void Test() + { + var sub = IFoo.CreateMock(); + // TODO: review CallInfo usage manually — Mockolate's Do/Returns take typed parameters, not CallInfo + sub.Mock.Setup.Bar(1).Do(call => Console.WriteLine(call)); + } + } + """); + [Fact] public async Task WhenMethod_WithArgMatcher_TransformsMatcher() => await Verifier.VerifyCodeFixAsync(