From 9fd07fb2d51b3abce7f4e48eb6ecd407d55e7f78 Mon Sep 17 00:00:00 2001 From: Meir Blachman Date: Wed, 17 Jan 2024 14:50:46 +0200 Subject: [PATCH] feat: nunit numeric assertions (#294) * feat: add nunit numeric assertions --- .../Tips/NunitTests.cs | 195 ++++++++++++++++++ .../Tips/NunitCodeFixProvider.cs | 88 +++++--- .../Tips/TestingFrameworkCodeFixProvider.cs | 3 + 3 files changed, 261 insertions(+), 25 deletions(-) diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs index df5b7ef2..9c8765e6 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs @@ -1,3 +1,4 @@ +using System; using FluentAssertions.Analyzers.TestUtils; using Microsoft.CodeAnalysis; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -192,6 +193,200 @@ public class NunitTests [Implemented] public void Nunit4_AssertNotNull_TestCodeFix(string oldAssertion, string newAssertion) => Nunit4VerifyFix("object actual", oldAssertion, newAssertion); + [DataTestMethod] + [AssertionDiagnostic("Assert.Greater(arg1, arg2{0});")] + [Implemented] + public void Nunit3_AssertGreater_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("ClassicAssert.Greater(arg1, arg2{0});")] + [Implemented] + public void Nunit4_AssertGreater_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "Assert.Greater(arg1, arg2{0});", + newAssertion: "arg1.Should().BeGreaterThan(arg2{0});")] + [Implemented] + public void Nunit3_AssertGreater_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "ClassicAssert.Greater(arg1, arg2{0});", + newAssertion: "arg1.Should().BeGreaterThan(arg2{0});")] + [Implemented] + public void Nunit4_AssertGreater_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("Assert.GreaterOrEqual(arg1, arg2{0});")] + [Implemented] + public void Nunit3_AssertGreaterOrEqual_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("ClassicAssert.GreaterOrEqual(arg1, arg2{0});")] + [Implemented] + public void Nunit4_AssertGreaterOrEqual_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "Assert.GreaterOrEqual(arg1, arg2{0});", + newAssertion: "arg1.Should().BeGreaterOrEqualTo(arg2{0});")] + [Implemented] + public void Nunit3_AssertGreaterOrEqual_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "ClassicAssert.GreaterOrEqual(arg1, arg2{0});", + newAssertion: "arg1.Should().BeGreaterOrEqualTo(arg2{0});")] + [Implemented] + public void Nunit4_AssertGreaterOrEqual_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("Assert.Less(arg1, arg2{0});")] + [Implemented] + public void Nunit3_AssertLess_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("ClassicAssert.Less(arg1, arg2{0});")] + [Implemented] + public void Nunit4_AssertLess_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "Assert.Less(arg1, arg2{0});", + newAssertion: "arg1.Should().BeLessThan(arg2{0});")] + [Implemented] + public void Nunit3_AssertLess_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "ClassicAssert.Less(arg1, arg2{0});", + newAssertion: "arg1.Should().BeLessThan(arg2{0});")] + [Implemented] + public void Nunit4_AssertLess_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("Assert.LessOrEqual(arg1, arg2{0});")] + [Implemented] + public void Nunit3_AssertLessOrEqual_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionDiagnostic("ClassicAssert.LessOrEqual(arg1, arg2{0});")] + [Implemented] + public void Nunit4_AssertLessOrEqual_TestAnalyzer(string assertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyDiagnostic(comparableArgument, assertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "Assert.LessOrEqual(arg1, arg2{0});", + newAssertion: "arg1.Should().BeLessOrEqualTo(arg2{0});")] + [Implemented] + public void Nunit3_AssertLessOrEqual_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit3VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "ClassicAssert.LessOrEqual(arg1, arg2{0});", + newAssertion: "arg1.Should().BeLessOrEqualTo(arg2{0});")] + [Implemented] + public void Nunit4_AssertLessOrEqual_TestCodeFix(string oldAssertion, string newAssertion) + { + foreach (var comparableArgument in ComparableArguments) + { + Nunit4VerifyFix(comparableArgument, oldAssertion, newAssertion); + } + } + + private static readonly string[] ComparableArguments = Array.ConvertAll(new string[] { "int", "uint", "long", "ulong", "float", "double", "decimal" }, x => $"{x} arg1, {x} arg2"); + private void Nunit3VerifyDiagnostic(string methodArguments, string assertion) => VerifyDiagnostic(GenerateCode.Nunit3Assertion(methodArguments, assertion), PackageReference.Nunit_3_14_0); private void Nunit3VerifyFix(string methodArguments, string oldAssertion, string newAssertion) diff --git a/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs b/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs index 237897c0..5e34cce4 100644 --- a/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs +++ b/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs @@ -22,15 +22,15 @@ protected override CreateChangedDocument TryComputeFixCore(IInvocationOperation return assertType.Name switch { - "Assert" when isNunit3 => TryComputeFixForNunit3Assert(invocation, context, t), - "ClassicAssert" when isNunit4 => TryComputeFixForNunit4ClassicAssert(invocation, context, t), + "Assert" when isNunit3 => TryComputeFixForNunitClassicAssert(invocation, context, t), + "ClassicAssert" when isNunit4 => TryComputeFixForNunitClassicAssert(invocation, context, t), //"StringAssert" => TryComputeFixForStringAssert(invocation, context, testContext), //"CollectionAssert" => TryComputeFixForCollectionAssert(invocation, context, testContext), _ => null }; } - private CreateChangedDocument TryComputeFixForNunit3Assert(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t) + private CreateChangedDocument TryComputeFixForNunitClassicAssert(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t) { switch (invocation.TargetMethod.Name) { @@ -46,29 +46,67 @@ private CreateChangedDocument TryComputeFixForNunit3Assert(IInvocationOperation case "NotNull": // Assert.NotNull(object anObject) case "IsNotNull": // Assert.IsNotNull(object anObject) return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "NotBeNull", subjectIndex: 0, argumentsToRemove: []); + case "Greater" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32): // Assert.Greater(int arg1, int arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32): // Assert.Greater(uint arg1, uint arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Long, t.Long): // Assert.Greater(long arg1, long arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong): // Assert.Greater(ulong arg1, ulong arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Float, t.Float): // Assert.Greater(float arg1, float arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Double, t.Double): // Assert.Greater(double arg1, double arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal): // Assert.Greater(decimal arg1, decimal arg2) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32, t.String, t.ObjectArray): // Assert.Greater(int arg1, int arg2, string message, params object[] parms) + case "Greater" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32, t.String, t.ObjectArray): // Assert.Greater(uint arg1, uint arg2, string message, params object[] parms) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Long, t.Long, t.String, t.ObjectArray): // Assert.Greater(long arg1, long arg2, string message, params object[] parms) + case "Greater" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong, t.String, t.ObjectArray): // Assert.Greater(ulong arg1, ulong arg2, string message, params object[] parms) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Float, t.Float, t.String, t.ObjectArray): // Assert.Greater(float arg1, float arg2, string message, params object[] parms) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Double, t.Double, t.String, t.ObjectArray): // Assert.Greater(double arg1, double arg2, string message, params object[] parms) + case "Greater" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal, t.String, t.ObjectArray): // Assert.Greater(decimal arg1, decimal arg2, string message, params object[] parms) + return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeGreaterThan", subjectIndex: 0, argumentsToRemove: []); + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32): // Assert.GreaterOrEqual(int arg1, int arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32): // Assert.GreaterOrEqual(uint arg1, uint arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Long, t.Long): // Assert.GreaterOrEqual(long arg1, long arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong): // Assert.GreaterOrEqual(ulong arg1, ulong arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Float, t.Float): // Assert.GreaterOrEqual(float arg1, float arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Double, t.Double): // Assert.GreaterOrEqual(double arg1, double arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal): // Assert.GreaterOrEqual(decimal arg1, decimal arg2) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32, t.String, t.ObjectArray): // Assert.GreaterOrEqual(int arg1, int arg2, string message, params object[] parms) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32, t.String, t.ObjectArray): // Assert.GreaterOrEqual(uint arg1, uint arg2, string message, params object[] parms) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Long, t.Long, t.String, t.ObjectArray): // Assert.GreaterOrEqual(long arg1, long arg2, string message, params object[] parms) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong, t.String, t.ObjectArray): // Assert.GreaterOrEqual(ulong arg1, ulong arg2, string message, params object[] parms) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Float, t.Float, t.String, t.ObjectArray): // Assert.GreaterOrEqual(float arg1, float arg2, string message, params object[] parms) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Double, t.Double, t.String, t.ObjectArray): // Assert.GreaterOrEqual(double arg1, double arg2, string message, params object[] parms) + case "GreaterOrEqual" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal, t.String, t.ObjectArray): // Assert.GreaterOrEqual(decimal arg1, decimal arg2, string message, params object[] parms) + return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeGreaterOrEqualTo", subjectIndex: 0, argumentsToRemove: []); + case "Less" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32): // Assert.Less(int arg1, int arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32): // Assert.Less(uint arg1, uint arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.Long, t.Long): // Assert.Less(long arg1, long arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong): // Assert.Less(ulong arg1, ulong arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.Float, t.Float): // Assert.Less(float arg1, float arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.Double, t.Double): // Assert.Less(double arg1, double arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal): // Assert.Less(decimal arg1, decimal arg2) + case "Less" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32, t.String, t.ObjectArray): // Assert.Less(int arg1, int arg2, string message, params object[] parms) + case "Less" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32, t.String, t.ObjectArray): // Assert.Less(uint arg1, uint arg2, string message, params object[] parms) + case "Less" when ArgumentsAreTypeOf(invocation, t.Long, t.Long, t.String, t.ObjectArray): // Assert.Less(long arg1, long arg2, string message, params object[] parms) + case "Less" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong, t.String, t.ObjectArray): // Assert.Less(ulong arg1, ulong arg2, string message, params object[] parms) + case "Less" when ArgumentsAreTypeOf(invocation, t.Float, t.Float, t.String, t.ObjectArray): // Assert.Less(float arg1, float arg2, string message, params object[] parms) + case "Less" when ArgumentsAreTypeOf(invocation, t.Double, t.Double, t.String, t.ObjectArray): // Assert.Less(double arg1, double arg2, string message, params object[] parms) + case "Less" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal, t.String, t.ObjectArray): // Assert.Less(decimal arg1, decimal arg2, string message, params object[] parms) + return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeLessThan", subjectIndex: 0, argumentsToRemove: []); + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32): // Assert.LessOrEqual(int arg1, int arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32): // Assert.LessOrEqual(uint arg1, uint arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Long, t.Long): // Assert.LessOrEqual(long arg1, long arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong): // Assert.LessOrEqual(ulong arg1, ulong arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Float, t.Float): // Assert.LessOrEqual(float arg1, float arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Double, t.Double): // Assert.LessOrEqual(double arg1, double arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal): // Assert.LessOrEqual(decimal arg1, decimal arg2) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Int32, t.Int32, t.String, t.ObjectArray): // Assert.LessOrEqual(int arg1, int arg2, string message, params object[] parms) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.UInt32, t.UInt32, t.String, t.ObjectArray): // Assert.LessOrEqual(uint arg1, uint arg2, string message, params object[] parms) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Long, t.Long, t.String, t.ObjectArray): // Assert.LessOrEqual(long arg1, long arg2, string message, params object[] parms) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.ULong, t.ULong, t.String, t.ObjectArray): // Assert.LessOrEqual(ulong arg1, ulong arg2, string message, params object[] parms) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Float, t.Float, t.String, t.ObjectArray): // Assert.LessOrEqual(float arg1, float arg2, string message, params object[] parms) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Double, t.Double, t.String, t.ObjectArray): // Assert.LessOrEqual(double arg1, double arg2, string message, params object[] parms) + case "LessOrEqual" when ArgumentsAreTypeOf(invocation, t.Decimal, t.Decimal, t.String, t.ObjectArray): // Assert.LessOrEqual(decimal arg1, decimal arg2, string message, params object[] parms) + return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeLessOrEqualTo", subjectIndex: 0, argumentsToRemove: []); } - - return null; - } - - private CreateChangedDocument TryComputeFixForNunit4ClassicAssert(IInvocationOperation invocation, CodeFixContext context, TestingFrameworkCodeFixContext t) - { - switch (invocation.TargetMethod.Name) - { - case "True": // Assert.True(bool condition) - case "IsTrue": // Assert.IsTrue(bool condition) - return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeTrue", subjectIndex: 0, argumentsToRemove: []); - case "False": // Assert.False(bool condition) - case "IsFalse": // Assert.IsFalse(bool condition) - return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeFalse", subjectIndex: 0, argumentsToRemove: []); - case "Null": // Assert.Null(object anObject) - case "IsNull": // Assert.IsNull(object anObject) - return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeNull", subjectIndex: 0, argumentsToRemove: []); - case "NotNull": // Assert.NotNull(object anObject) - case "IsNotNull": // Assert.IsNotNull(object anObject) - return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "NotBeNull", subjectIndex: 0, argumentsToRemove: []); - } - return null; } } \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers/Tips/TestingFrameworkCodeFixProvider.cs b/src/FluentAssertions.Analyzers/Tips/TestingFrameworkCodeFixProvider.cs index e05170df..ec314c92 100644 --- a/src/FluentAssertions.Analyzers/Tips/TestingFrameworkCodeFixProvider.cs +++ b/src/FluentAssertions.Analyzers/Tips/TestingFrameworkCodeFixProvider.cs @@ -122,6 +122,9 @@ public sealed class TestingFrameworkCodeFixContext(Compilation compilation) public INamedTypeSymbol Object { get; } = compilation.ObjectType; public INamedTypeSymbol String { get; } = compilation.GetTypeByMetadataName("System.String"); public INamedTypeSymbol Int32 { get; } = compilation.GetTypeByMetadataName("System.Int32"); + public INamedTypeSymbol UInt32 { get; } = compilation.GetTypeByMetadataName("System.UInt32"); + public INamedTypeSymbol Long { get; } = compilation.GetTypeByMetadataName("System.Int64"); + public INamedTypeSymbol ULong { get; } = compilation.GetTypeByMetadataName("System.UInt64"); public INamedTypeSymbol Float { get; } = compilation.GetTypeByMetadataName("System.Single"); public INamedTypeSymbol Double { get; } = compilation.GetTypeByMetadataName("System.Double"); public INamedTypeSymbol Decimal { get; } = compilation.GetTypeByMetadataName("System.Decimal");