diff --git a/docs/Nunit3Analyzer.md b/docs/Nunit3Analyzer.md index e15fc2b..a84b361 100644 --- a/docs/Nunit3Analyzer.md +++ b/docs/Nunit3Analyzer.md @@ -14,6 +14,10 @@ This is a generated file, please edit src\FluentAssertions.Analyzers.FluentAsser - [AssertNotZero](#scenario-assertnotzero) - `number.Should().NotBe(0);` - [CollectionAssertAreEqual](#scenario-collectionassertareequal) - `collection.Should().Equal(expected);` - [CollectionAssertAreNotEqual](#scenario-collectionassertarenotequal) - `collection.Should().NotEqual(expected);` +- [CollectionAssertContains](#scenario-collectionassertcontains) - `collection.Should().Contain(2);` +- [CollectionAssertContains_WithCasting](#scenario-collectionassertcontains_withcasting) - `collection.Should().Contain((int)item);` +- [CollectionAssertDoesNotContain](#scenario-collectionassertdoesnotcontain) - `collection.Should().NotContain(4);` +- [CollectionAssertDoesNotContain_WithCasting](#scenario-collectionassertdoesnotcontain_withcasting) - `collection.Should().NotContain((int)item);` ## Scenarios @@ -362,4 +366,116 @@ CollectionAssert.AreNotEqual(expected, collection); /* fail message: Expected: collection.Should().NotEqual(expected); /* fail message: Did not expect collections {1, 2, 3} and {1, 2, 3} to be equal. */ ``` +### scenario: CollectionAssertContains + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.Contains(collection, 2); + +// new assertion: +collection.Should().Contain(2); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.Contains(collection, 4); /* fail message: Expected: some item equal to 4 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().Contain(4); /* fail message: Expected collection {1, 2, 3} to contain 4. */ +``` + +### scenario: CollectionAssertContains_WithCasting + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; +object item = 2; + +// old assertion: +CollectionAssert.Contains(collection, item); + +// new assertion: +collection.Should().Contain((int)item); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; +object item = 4; + +// old assertion: +CollectionAssert.Contains(collection, item); /* fail message: Expected: some item equal to 4 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().Contain((int)item); /* fail message: Expected collection {1, 2, 3} to contain 4. */ +``` + +### scenario: CollectionAssertDoesNotContain + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.DoesNotContain(collection, 4); + +// new assertion: +collection.Should().NotContain(4); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.DoesNotContain(collection, 2); /* fail message: Expected: not some item equal to 2 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().NotContain(2); /* fail message: Expected collection {1, 2, 3} to not contain 2. */ +``` + +### scenario: CollectionAssertDoesNotContain_WithCasting + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; +object item = 4; + +// old assertion: +CollectionAssert.DoesNotContain(collection, item); + +// new assertion: +collection.Should().NotContain((int)item); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; +object item = 2; + +// old assertion: +CollectionAssert.DoesNotContain(collection, item); /* fail message: Expected: not some item equal to 2 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().NotContain((int)item); /* fail message: Expected collection {1, 2, 3} to not contain 2. */ +``` + diff --git a/docs/Nunit4Analyzer.md b/docs/Nunit4Analyzer.md index 79353c2..acac8d7 100644 --- a/docs/Nunit4Analyzer.md +++ b/docs/Nunit4Analyzer.md @@ -14,6 +14,10 @@ This is a generated file, please edit src\FluentAssertions.Analyzers.FluentAsser - [AssertNotZero](#scenario-assertnotzero) - `number.Should().NotBe(0);` - [CollectionAssertAreEqual](#scenario-collectionassertareequal) - `collection.Should().Equal(expected);` - [CollectionAssertAreNotEqual](#scenario-collectionassertarenotequal) - `collection.Should().NotEqual(expected);` +- [CollectionAssertContains](#scenario-collectionassertcontains) - `collection.Should().Contain(2);` +- [CollectionAssertContains_WithCasting](#scenario-collectionassertcontains_withcasting) - `collection.Should().Contain((int)item);` +- [CollectionAssertDoesNotContain](#scenario-collectionassertdoesnotcontain) - `collection.Should().NotContain(4);` +- [CollectionAssertDoesNotContain_WithCasting](#scenario-collectionassertdoesnotcontain_withcasting) - `collection.Should().NotContain((int)item);` ## Scenarios @@ -389,4 +393,120 @@ CollectionAssert.AreNotEqual(expected, collection); /* fail message: Assert.Th collection.Should().NotEqual(expected); /* fail message: Did not expect collections {1, 2, 3} and {1, 2, 3} to be equal. */ ``` +### scenario: CollectionAssertContains + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.Contains(collection, 2); + +// new assertion: +collection.Should().Contain(2); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.Contains(collection, 4); /* fail message: Assert.That(collection, Has.Member(actual)) + Expected: some item equal to 4 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().Contain(4); /* fail message: Expected collection {1, 2, 3} to contain 4. */ +``` + +### scenario: CollectionAssertContains_WithCasting + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; +object item = 2; + +// old assertion: +CollectionAssert.Contains(collection, item); + +// new assertion: +collection.Should().Contain((int)item); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; +object item = 4; + +// old assertion: +CollectionAssert.Contains(collection, item); /* fail message: Assert.That(collection, Has.Member(actual)) + Expected: some item equal to 4 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().Contain((int)item); /* fail message: Expected collection {1, 2, 3} to contain 4. */ +``` + +### scenario: CollectionAssertDoesNotContain + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.DoesNotContain(collection, 4); + +// new assertion: +collection.Should().NotContain(4); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; + +// old assertion: +CollectionAssert.DoesNotContain(collection, 2); /* fail message: Assert.That(collection, Has.No.Member(actual)) + Expected: not some item equal to 2 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().NotContain(2); /* fail message: Expected collection {1, 2, 3} to not contain 2. */ +``` + +### scenario: CollectionAssertDoesNotContain_WithCasting + +```cs +// arrange +var collection = new[] { 1, 2, 3 }; +object item = 4; + +// old assertion: +CollectionAssert.DoesNotContain(collection, item); + +// new assertion: +collection.Should().NotContain((int)item); +``` + +#### Failure messages + +```cs +var collection = new[] { 1, 2, 3 }; +object item = 2; + +// old assertion: +CollectionAssert.DoesNotContain(collection, item); /* fail message: Assert.That(collection, Has.No.Member(actual)) + Expected: not some item equal to 2 + But was: < 1, 2, 3 > + */ + +// new assertion: +collection.Should().NotContain((int)item); /* fail message: Expected collection {1, 2, 3} to not contain 2. */ +``` + diff --git a/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs.Nunit4/Nunit4AnalyzerTests.cs b/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs.Nunit4/Nunit4AnalyzerTests.cs index ada71bd..bb90f8f 100644 --- a/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs.Nunit4/Nunit4AnalyzerTests.cs +++ b/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs.Nunit4/Nunit4AnalyzerTests.cs @@ -529,4 +529,142 @@ public void CollectionAssertAreNotEqual_Failure_NewAssertion() // new assertion: collection.Should().NotEqual(expected); } + + [Test] + public void CollectionAssertContains() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.Contains(collection, 2); + + // new assertion: + collection.Should().Contain(2); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertContains_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.Contains(collection, 4); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertContains_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // new assertion: + collection.Should().Contain(4); + } + + [Test] + public void CollectionAssertContains_WithCasting() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 2; + + // old assertion: + CollectionAssert.Contains(collection, item); + + // new assertion: + collection.Should().Contain((int)item); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertContains_WithCasting_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 4; + + // old assertion: + CollectionAssert.Contains(collection, item); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertContains_WithCasting_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 4; + + // new assertion: + collection.Should().Contain((int)item); + } + + [Test] + public void CollectionAssertDoesNotContain() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.DoesNotContain(collection, 4); + + // new assertion: + collection.Should().NotContain(4); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertDoesNotContain_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.DoesNotContain(collection, 2); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertDoesNotContain_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // new assertion: + collection.Should().NotContain(2); + } + + [Test] + public void CollectionAssertDoesNotContain_WithCasting() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 4; + + // old assertion: + CollectionAssert.DoesNotContain(collection, item); + + // new assertion: + collection.Should().NotContain((int)item); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertDoesNotContain_WithCasting_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 2; + + // old assertion: + CollectionAssert.DoesNotContain(collection, item); + } + + [Test, ExpectedAssertionException] + public void CollectionAssertDoesNotContain_WithCasting_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 2; + + // new assertion: + collection.Should().NotContain((int)item); + } } \ No newline at end of file diff --git a/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs/Nunit3AnalyzerTests.cs b/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs/Nunit3AnalyzerTests.cs index aff76cc..5b94240 100644 --- a/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs/Nunit3AnalyzerTests.cs +++ b/src/FluentAssertions.Analyzers.FluentAssertionAnalyzerDocs/Nunit3AnalyzerTests.cs @@ -532,4 +532,142 @@ public void CollectionAssertAreNotEqual_Failure_NewAssertion() // new assertion: collection.Should().NotEqual(expected); } + + [TestMethod] + public void CollectionAssertContains() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.Contains(collection, 2); + + // new assertion: + collection.Should().Contain(2); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertContains_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.Contains(collection, 4); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertContains_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // new assertion: + collection.Should().Contain(4); + } + + [TestMethod] + public void CollectionAssertContains_WithCasting() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 2; + + // old assertion: + CollectionAssert.Contains(collection, item); + + // new assertion: + collection.Should().Contain((int)item); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertContains_WithCasting_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 4; + + // old assertion: + CollectionAssert.Contains(collection, item); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertContains_WithCasting_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 4; + + // new assertion: + collection.Should().Contain((int)item); + } + + [TestMethod] + public void CollectionAssertDoesNotContain() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.DoesNotContain(collection, 4); + + // new assertion: + collection.Should().NotContain(4); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertDoesNotContain_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // old assertion: + CollectionAssert.DoesNotContain(collection, 2); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertDoesNotContain_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + + // new assertion: + collection.Should().NotContain(2); + } + + [TestMethod] + public void CollectionAssertDoesNotContain_WithCasting() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 4; + + // old assertion: + CollectionAssert.DoesNotContain(collection, item); + + // new assertion: + collection.Should().NotContain((int)item); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertDoesNotContain_WithCasting_Failure_OldAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 2; + + // old assertion: + CollectionAssert.DoesNotContain(collection, item); + } + + [TestMethod, ExpectedTestFrameworkException] + public void CollectionAssertDoesNotContain_WithCasting_Failure_NewAssertion() + { + // arrange + var collection = new[] { 1, 2, 3 }; + object item = 2; + + // new assertion: + collection.Should().NotContain((int)item); + } } diff --git a/src/FluentAssertions.Analyzers.Tests/TestAttributes.cs b/src/FluentAssertions.Analyzers.Tests/TestAttributes.cs index 31ad162..c43a275 100644 --- a/src/FluentAssertions.Analyzers.Tests/TestAttributes.cs +++ b/src/FluentAssertions.Analyzers.Tests/TestAttributes.cs @@ -66,6 +66,31 @@ public IEnumerable GetData(MethodInfo methodInfo) public string GetDisplayName(MethodInfo methodInfo, object[] data) => $"{methodInfo.Name}(\"old: {data[0]}\", new: {data[1]}\")"; } +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] +public class AssertionMethodCodeFixAttribute : Attribute, ITestDataSource +{ + public string MethodArguments { get; } + public string OldAssertion { get; } + public string NewAssertion { get; } + + public bool Ignore { get; } + + public AssertionMethodCodeFixAttribute(string methodArguments, string oldAssertion, string newAssertion, bool ignore = false) + => (MethodArguments, OldAssertion, NewAssertion, Ignore) = (methodArguments, oldAssertion, newAssertion, ignore); + + public IEnumerable GetData(MethodInfo methodInfo) + { + if (Ignore) yield break; + + foreach (var (oldAssertion, newAssertion) in TestCasesInputUtils.GetTestCases(OldAssertion, NewAssertion)) + { + yield return new object[] { MethodArguments, oldAssertion, newAssertion }; + } + } + + public string GetDisplayName(MethodInfo methodInfo, object[] data) => $"{methodInfo.Name}(\"arguments\":{data[0]}, \"old: {data[1]}\", new: {data[2]}\")"; +} + public static class TestCasesInputUtils { private static readonly string Empty = string.Empty; diff --git a/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs b/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs index 515ca8e..35f06d5 100644 --- a/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs +++ b/src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs @@ -1154,47 +1154,51 @@ public void Nunit4_AssertContains_TestCodeFix(string oldAssertion, string newAss } [DataTestMethod] - [DataRow( - /* methodArguments: */ "object expected, string[] actual", - /* oldAssertion: */ "Assert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((string)expected);")] - [DataRow( - /* methodArguments: */ "object expected, List actual", - /* oldAssertion: */ "Assert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((string)expected);")] - [DataRow( - /* methodArguments: */ "object expected, DateTime[] actual", - /* oldAssertion: */ "Assert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((DateTime)expected);")] - [DataRow( - /* methodArguments: */ "object expected, List actual", - /* oldAssertion: */ "Assert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, string[] actual", + oldAssertion: "Assert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "Assert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, DateTime[] actual", + oldAssertion: "Assert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "Assert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] [Implemented] public void Nunit3_AssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion) => Nunit3VerifyFix(methodArguments, oldAssertion, newAssertion); [DataTestMethod] - [DataRow( - /* methodArguments: */ "object expected, string[] actual", - /* oldAssertion: */ "ClassicAssert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((string)expected);")] - [DataRow( - /* methodArguments: */ "object expected, List actual", - /* oldAssertion: */ "ClassicAssert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((string)expected);")] - [DataRow( - /* methodArguments: */ "object expected, DateTime[] actual", - /* oldAssertion: */ "ClassicAssert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((DateTime)expected);")] - [DataRow( - /* methodArguments: */ "object expected, List actual", - /* oldAssertion: */ "ClassicAssert.Contains(expected, actual);", - /* newAssertion: */ "actual.Should().Contain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, string[] actual", + oldAssertion: "ClassicAssert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "ClassicAssert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, DateTime[] actual", + oldAssertion: "ClassicAssert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "ClassicAssert.Contains(expected, actual);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] [Implemented] public void Nunit4_AssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion) => Nunit4VerifyFix(methodArguments, oldAssertion, newAssertion); + #endregion + + #region CollectionAssert.cs + [DataTestMethod] [AssertionDiagnostic("CollectionAssert.AreEqual(expected, actual{0});")] [AssertionDiagnostic("Assert.That(actual, Is.EqualTo(expected){0});", ignore: true)] @@ -1303,6 +1307,210 @@ public void Nunit4_CollectionAssertAreNotEqual_TestCodeFix(string oldAssertion, Nunit4VerifyFix("ICollection expected, ICollection actual", oldAssertion, newAssertion); } + [DataTestMethod] + [AssertionDiagnostic("CollectionAssert.Contains(actual, expected{0});")] + [Implemented] + public void Nunit3_CollectionAssertContains_TestAnalyzer(string assertion) + { + Nunit3VerifyDiagnostic("object expected, IEnumerable actual", assertion); + Nunit3VerifyDiagnostic("object expected, string[] actual", assertion); + Nunit3VerifyDiagnostic("object expected, List actual", assertion); + Nunit3VerifyDiagnostic("object expected, object[] actual", assertion); + Nunit3VerifyDiagnostic("object expected, List actual", assertion); + Nunit3VerifyDiagnostic("DateTime expected, DateTime[] actual", assertion); + Nunit3VerifyDiagnostic("DateTime expected, List actual", assertion); + } + + [DataTestMethod] + [AssertionDiagnostic("CollectionAssert.Contains(actual, expected{0});")] + [Implemented] + public void Nunit4_CollectionAssertContains_TestAnalyzer(string assertion) + { + Nunit4VerifyDiagnostic("object expected, IEnumerable actual", assertion); + Nunit4VerifyDiagnostic("object expected, string[] actual", assertion); + Nunit4VerifyDiagnostic("object expected, List actual", assertion); + Nunit4VerifyDiagnostic("object expected, object[] actual", assertion); + Nunit4VerifyDiagnostic("object expected, List actual", assertion); + Nunit4VerifyDiagnostic("DateTime expected, DateTime[] actual", assertion); + Nunit4VerifyDiagnostic("DateTime expected, List actual", assertion); + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "CollectionAssert.Contains(actual, expected{0});", + newAssertion: "actual.Should().Contain(expected{0});")] + [Implemented] + public void Nunit3_CollectionAssertContains_TestCodeFix(string oldAssertion, string newAssertion) + { + Nunit3VerifyNoFix("object expected, IEnumerable actual", oldAssertion); + Nunit3VerifyFix("string expected, string[] actual", oldAssertion, newAssertion); + Nunit3VerifyFix("string expected, List actual", oldAssertion, newAssertion); + Nunit3VerifyFix("object expected, object[] actual", oldAssertion, newAssertion); + Nunit3VerifyFix("object expected, List actual", oldAssertion, newAssertion); + Nunit3VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion); + Nunit3VerifyFix("DateTime expected, List actual", oldAssertion, newAssertion); + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "CollectionAssert.Contains(actual, expected{0});", + newAssertion: "actual.Should().Contain(expected{0});")] + [Implemented] + public void Nunit4_CollectionAssertContains_TestCodeFix(string oldAssertion, string newAssertion) + { + Nunit4VerifyNoFix("object expected, IEnumerable actual", oldAssertion); + Nunit4VerifyFix("string expected, string[] actual", oldAssertion, newAssertion); + Nunit4VerifyFix("string expected, List actual", oldAssertion, newAssertion); + Nunit4VerifyFix("object expected, object[] actual", oldAssertion, newAssertion); + Nunit4VerifyFix("object expected, List actual", oldAssertion, newAssertion); + Nunit4VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion); + Nunit4VerifyFix("DateTime expected, List actual", oldAssertion, newAssertion); + } + + [DataTestMethod] + [AssertionMethodCodeFix( + methodArguments: "object expected, string[] actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, DateTime[] actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] + [Implemented] + public void Nunit3_CollectionAssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion) + => Nunit3VerifyFix(methodArguments, oldAssertion, newAssertion); + + [DataTestMethod] + [AssertionMethodCodeFix( + methodArguments: "object expected, string[] actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, DateTime[] actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.Contains(actual, expected);", + newAssertion: "actual.Should().Contain((DateTime)expected);")] + [Implemented] + public void Nunit4_CollectionAssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion) + => Nunit4VerifyFix(methodArguments, oldAssertion, newAssertion); + + [DataTestMethod] + [AssertionDiagnostic("CollectionAssert.DoesNotContain(actual, expected{0});")] + [Implemented] + public void Nunit3_CollectionAssertDoesNotContain_TestAnalyzer(string assertion) + { + Nunit3VerifyDiagnostic("object expected, IEnumerable actual", assertion); + Nunit3VerifyDiagnostic("object expected, string[] actual", assertion); + Nunit3VerifyDiagnostic("object expected, List actual", assertion); + Nunit3VerifyDiagnostic("object expected, object[] actual", assertion); + Nunit3VerifyDiagnostic("object expected, List actual", assertion); + Nunit3VerifyDiagnostic("DateTime expected, DateTime[] actual", assertion); + Nunit3VerifyDiagnostic("DateTime expected, List actual", assertion); + } + + [DataTestMethod] + [AssertionDiagnostic("CollectionAssert.DoesNotContain(actual, expected{0});")] + [Implemented] + public void Nunit4_CollectionAssertDoesNotContain_TestAnalyzer(string assertion) + { + Nunit4VerifyDiagnostic("object expected, IEnumerable actual", assertion); + Nunit4VerifyDiagnostic("object expected, string[] actual", assertion); + Nunit4VerifyDiagnostic("string expected, List actual", assertion); + Nunit4VerifyDiagnostic("object expected, object[] actual", assertion); + Nunit4VerifyDiagnostic("object expected, List actual", assertion); + Nunit4VerifyDiagnostic("DateTime expected, DateTime[] actual", assertion); + Nunit4VerifyDiagnostic("DateTime expected, List actual", assertion); + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected{0});", + newAssertion: "actual.Should().NotContain(expected{0});")] + [Implemented] + public void Nunit3_CollectionAssertDoesNotContain_TestCodeFix(string oldAssertion, string newAssertion) + { + Nunit3VerifyNoFix("object expected, IEnumerable actual", oldAssertion); + Nunit3VerifyFix("string expected, string[] actual", oldAssertion, newAssertion); + Nunit3VerifyFix("string expected, List actual", oldAssertion, newAssertion); + Nunit3VerifyFix("object expected, object[] actual", oldAssertion, newAssertion); + Nunit3VerifyFix("object expected, List actual", oldAssertion, newAssertion); + Nunit3VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion); + Nunit3VerifyFix("DateTime expected, List actual", oldAssertion, newAssertion); + } + + [DataTestMethod] + [AssertionCodeFix( + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected{0});", + newAssertion: "actual.Should().NotContain(expected{0});")] + [Implemented] + public void Nunit4_CollectionAssertDoesNotContain_TestCodeFix(string oldAssertion, string newAssertion) + { + Nunit4VerifyNoFix("object expected, IEnumerable actual", oldAssertion); + Nunit4VerifyFix("string expected, string[] actual", oldAssertion, newAssertion); + Nunit4VerifyFix("string expected, List actual", oldAssertion, newAssertion); + Nunit4VerifyFix("object expected, object[] actual", oldAssertion, newAssertion); + Nunit4VerifyFix("object expected, List actual", oldAssertion, newAssertion); + Nunit4VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion); + Nunit4VerifyFix("DateTime expected, List actual", oldAssertion, newAssertion); + } + + [DataTestMethod] + [AssertionMethodCodeFix( + methodArguments: "object expected, string[] actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, DateTime[] actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((DateTime)expected);")] + [Implemented] + public void Nunit3_CollectionAssertDoesNotContain_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion) + => Nunit3VerifyFix(methodArguments, oldAssertion, newAssertion); + + [DataTestMethod] + [AssertionMethodCodeFix( + methodArguments: "object expected, string[] actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((string)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, DateTime[] actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((DateTime)expected);")] + [AssertionMethodCodeFix( + methodArguments: "object expected, List actual", + oldAssertion: "CollectionAssert.DoesNotContain(actual, expected);", + newAssertion: "actual.Should().NotContain((DateTime)expected);")] + [Implemented] + public void Nunit4_CollectionAssertDoesNotContain_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion) + => Nunit4VerifyFix(methodArguments, oldAssertion, newAssertion); + #endregion private void Nunit3VerifyDiagnostic(string methodArguments, string assertion) diff --git a/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs b/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs index c98855c..e1d5a75 100644 --- a/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs +++ b/src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs @@ -7,6 +7,7 @@ using System; using FluentAssertions.Analyzers.Utilities; using Microsoft.CodeAnalysis.Simplification; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace FluentAssertions.Analyzers; @@ -197,27 +198,9 @@ private CreateChangedDocument TryComputeFixForNunitClassicAssert(IInvocationOper return DocumentEditorUtils.RenameGenericMethodToSubjectShouldGenericAssertion(invocation, context, "NotBeOfType", subjectIndex: 0, argumentsToRemove: []); case "Contains": // Assert.Contains(object expected, ICollection actual) { - var collectionArgument = invocation.Arguments[1].Value.UnwrapConversion(); - if (collectionArgument.Type.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_IEnumerable_T)) - { - return DocumentEditorUtils.RewriteExpression(invocation, [ - (EditActionContext editActionContext) => - { - ITypeSymbol elementType = collectionArgument.Type switch - { - INamedTypeSymbol namedType => namedType.TypeArguments[0], - IArrayTypeSymbol arrayType => arrayType.ElementType, - _ => null - }; - - var argumentToCast = editActionContext.InvocationExpression.ArgumentList.Arguments[0]; - var castExpression = editActionContext.Editor.Generator.CastExpression(elementType, argumentToCast.Expression); - editActionContext.Editor.ReplaceNode(argumentToCast.Expression, castExpression.WithAdditionalAnnotations(Simplifier.Annotation)); - }, - EditAction.SubjectShouldAssertion(argumentIndex: 1, "Contain") - ], context); - } - return null; + var subject = invocation.Arguments[1]; + var expected = invocation.Arguments[0]; + return RewriteContainsAssertion(invocation, context, "Contain", subject, expected); } } return null; @@ -229,12 +212,22 @@ private CreateChangedDocument TryComputeFixForCollectionAssert(IInvocationOperat { case "IsEmpty" when !IsArgumentTypeOfNonGenericEnumerable(invocation, argumentIndex: 0): // CollectionAssert.IsEmpty(IEnumerable collection) return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "BeEmpty", subjectIndex: 0, argumentsToRemove: []); - case "IsNotEmpty" when !IsArgumentTypeOfNonGenericEnumerable(invocation, argumentIndex: 0) : // CollectionAssert.IsNotEmpty(IEnumerable collection) + case "IsNotEmpty" when !IsArgumentTypeOfNonGenericEnumerable(invocation, argumentIndex: 0): // CollectionAssert.IsNotEmpty(IEnumerable collection) return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "NotBeEmpty", subjectIndex: 0, argumentsToRemove: []); case "AreEqual" when !IsArgumentTypeOfNonGenericEnumerable(invocation, argumentIndex: 0): // CollectionAssert.AreEqual(IEnumerable expected, IEnumerable actual) return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "Equal", subjectIndex: 1, argumentsToRemove: []); case "AreNotEqual" when !IsArgumentTypeOfNonGenericEnumerable(invocation, argumentIndex: 0): // CollectionAssert.AreNotEqual(IEnumerable notExpected, IEnumerable actual) return DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, "NotEqual", subjectIndex: 1, argumentsToRemove: []); + case "Contains": // CollectionAssert.Contain(IEnumerable collection, object actual) + return RewriteContainsAssertion(invocation, context, "Contain", + subject: invocation.Arguments[0], + expectation: invocation.Arguments[1] + ); + case "DoesNotContain": // CollectionAssert.DoesNotContain(IEnumerable collection, object actual) + return RewriteContainsAssertion(invocation, context, "NotContain", + subject: invocation.Arguments[0], + expectation: invocation.Arguments[1] + ); } return null; } @@ -291,6 +284,32 @@ CreateChangedDocument RenameAssertThatAssertionToSubjectShouldAssertion(string a => DocumentEditorUtils.RenameMethodToSubjectShouldAssertion(invocation, context, assertionName, subjectIndex: 0, argumentsToRemove: [1]); } + private CreateChangedDocument RewriteContainsAssertion(IInvocationOperation invocation, CodeFixContext context, string assertion, IArgumentOperation subject, IArgumentOperation expectation) + { + if (!subject.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_IEnumerable_T)) + { + return null; + } + + var subjectIndex = invocation.Arguments.IndexOf(subject); + return DocumentEditorUtils.RewriteExpression(invocation, [ + editActionContext => + { + ITypeSymbol elementType = subject.Value.UnwrapConversion().Type switch + { + INamedTypeSymbol namedType => namedType.TypeArguments[0], + IArrayTypeSymbol arrayType => arrayType.ElementType, + _ => null + }; + + var argumentToCast = (ArgumentSyntax)expectation.Syntax; + var castExpression = editActionContext.Editor.Generator.CastExpression(elementType, argumentToCast.Expression); + editActionContext.Editor.ReplaceNode(argumentToCast.Expression, castExpression.WithAdditionalAnnotations(Simplifier.Annotation)); + }, + EditAction.SubjectShouldAssertion(subjectIndex, assertion) + ], context); + } + private static bool IsPropertyReferencedFromType(IPropertyReferenceOperation propertyReference, INamedTypeSymbol type) => propertyReference.Property.ContainingType.EqualsSymbol(type); private static bool IsPropertyOfSymbol(IPropertyReferenceOperation propertyReference, string firstProperty, string secondProperty, INamedTypeSymbol type) @@ -301,6 +320,7 @@ private static bool IsPropertyOfSymbol(IOperation operation, string property, IN private static bool IsArgumentTypeOfNonGenericEnumerable(IInvocationOperation invocation, int argumentIndex) => IsArgumentTypeOf(invocation, argumentIndex, SpecialType.System_Collections_IEnumerable); private static bool IsArgumentTypeOf(IInvocationOperation invocation, int argumentIndex, SpecialType specialType) => invocation.Arguments[argumentIndex].Value.UnwrapConversion().Type.SpecialType == specialType; + public class NunitCodeFixContext(Compilation compilation) : TestingFrameworkCodeFixProvider.TestingFrameworkCodeFixContext(compilation) { public INamedTypeSymbol Is { get; } = compilation.GetTypeByMetadataName("NUnit.Framework.Is");