Skip to content

Commit ed6aec1

Browse files
authored
feat: add nunit contains assertions (#297)
1 parent 68da99d commit ed6aec1

File tree

4 files changed

+160
-2
lines changed

4 files changed

+160
-2
lines changed

src/FluentAssertions.Analyzers.TestUtils/GenerateCode.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ public static string GenericIListExpressionBodyAssertion(string assertion) => Ge
236236

237237
public static string Nunit3Assertion(string methodArguments, string assertion) => new StringBuilder()
238238
.AppendLine("using System;")
239+
.AppendLine("using System.Collections;")
239240
.AppendLine("using System.Collections.Generic;")
240241
.AppendLine("using System.Collections.Immutable;")
241242
.AppendLine("using System.Text.RegularExpressions;")
@@ -257,6 +258,7 @@ public static string GenericIListExpressionBodyAssertion(string assertion) => Ge
257258

258259
public static string Nunit4Assertion(string methodArguments, string assertion) => new StringBuilder()
259260
.AppendLine("using System;")
261+
.AppendLine("using System.Collections;")
260262
.AppendLine("using System.Collections.Generic;")
261263
.AppendLine("using System.Collections.Immutable;")
262264
.AppendLine("using System.Text.RegularExpressions;")

src/FluentAssertions.Analyzers.Tests/DiagnosticVerifier.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,24 @@ public static void VerifyCSharpFix<TCodeFixProvider, TDiagnosticAnalyzer>(string
3838
public static void VerifyFix(CodeFixVerifierArguments arguments)
3939
=> VerifyFix(arguments, arguments.DiagnosticAnalyzers.Single(), arguments.CodeFixProviders.Single(), arguments.FixedSources.Single());
4040

41+
public static void VerifyNoFix(CodeFixVerifierArguments arguments)
42+
=> VerifyNoFix(arguments, arguments.DiagnosticAnalyzers.Single(), arguments.CodeFixProviders.Single());
43+
private static void VerifyNoFix(CsProjectArguments arguments, DiagnosticAnalyzer analyzer, CodeFixProvider codeFixProvider)
44+
{
45+
var document = CsProjectGenerator.CreateDocument(arguments);
46+
var analyzerDiagnostics = GetSortedDiagnosticsFromDocuments(new[] { analyzer }, new[] { document });
47+
var compilerDiagnostics = GetCompilerDiagnostics(document);
48+
var attempts = analyzerDiagnostics.Length;
49+
50+
for (int i = 0; i < attempts; ++i)
51+
{
52+
var actions = new List<CodeAction>();
53+
var context = new CodeFixContext(document, analyzerDiagnostics[0], (a, d) => actions.Add(a), CancellationToken.None);
54+
codeFixProvider.RegisterCodeFixesAsync(context).Wait();
55+
56+
actions.Should().BeEmpty("There should be no code fix actions available to suppress the diagnostic.");
57+
}
58+
}
4159

4260
/// <summary>
4361
/// General verifier for codefixes.

src/FluentAssertions.Analyzers.Tests/Tips/NunitTests.cs

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -667,16 +667,119 @@ public void Nunit4_AssertIsNotInstanceOf_TestCodeFix(string oldAssertion, string
667667

668668
#endregion
669669

670+
#region Assert.Contains.cs
671+
672+
[DataTestMethod]
673+
[AssertionDiagnostic("Assert.Contains(expected, actual{0});")]
674+
[Implemented]
675+
public void Nunit3_AssertContains_ICollection_TestCodeNoFix(string assertion)
676+
=> Nunit3VerifyNoFix("object expected, ICollection actual", assertion);
677+
678+
[DataTestMethod]
679+
[AssertionDiagnostic("ClassicAssert.Contains(expected, actual{0});")]
680+
[Implemented]
681+
public void Nunit4_AssertContains_ICollection_TestCodeNoFix(string assertion)
682+
=> Nunit4VerifyNoFix("object expected, ICollection actual", assertion);
683+
684+
[DataTestMethod]
685+
[AssertionDiagnostic("Assert.Contains(expected, actual{0});")]
686+
[Implemented]
687+
public void Nunit3_AssertContains_TestAnalyzer(string assertion)
688+
=> Nunit3VerifyDiagnostic("object expected, string[] actual", assertion);
689+
690+
[DataTestMethod]
691+
[AssertionDiagnostic("ClassicAssert.Contains(expected, actual{0});")]
692+
[Implemented]
693+
public void Nunit4_AssertContains_TestAnalyzer(string assertion)
694+
=> Nunit4VerifyDiagnostic("object expected, string[] actual", assertion);
695+
696+
[DataTestMethod]
697+
[AssertionCodeFix(
698+
oldAssertion: "Assert.Contains(expected, actual{0});",
699+
newAssertion: "actual.Should().Contain(expected{0});")]
700+
[Implemented]
701+
public void Nunit3_AssertContains_TestCodeFix(string oldAssertion, string newAssertion)
702+
{
703+
Nunit3VerifyFix("string expected, string[] actual", oldAssertion, newAssertion);
704+
Nunit3VerifyFix("string expected, List<string> actual", oldAssertion, newAssertion);
705+
Nunit3VerifyFix("string expected, object[] actual", oldAssertion, newAssertion);
706+
Nunit3VerifyFix("string expected, List<object> actual", oldAssertion, newAssertion);
707+
Nunit3VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion);
708+
Nunit3VerifyFix("DateTime expected, List<DateTime> actual", oldAssertion, newAssertion);
709+
}
710+
711+
[DataTestMethod]
712+
[AssertionCodeFix(
713+
oldAssertion: "ClassicAssert.Contains(expected, actual{0});",
714+
newAssertion: "actual.Should().Contain(expected{0});")]
715+
[Implemented]
716+
public void Nunit4_AssertContains_TestCodeFix(string oldAssertion, string newAssertion)
717+
{
718+
Nunit4VerifyFix("string expected, string[] actual", oldAssertion, newAssertion);
719+
Nunit4VerifyFix("string expected, List<string> actual", oldAssertion, newAssertion);
720+
Nunit4VerifyFix("string expected, object[] actual", oldAssertion, newAssertion);
721+
Nunit4VerifyFix("string expected, List<object> actual", oldAssertion, newAssertion);
722+
Nunit4VerifyFix("DateTime expected, DateTime[] actual", oldAssertion, newAssertion);
723+
Nunit4VerifyFix("DateTime expected, List<DateTime> actual", oldAssertion, newAssertion);
724+
}
725+
726+
[DataTestMethod]
727+
[DataRow(
728+
/* methodArguments: */ "object expected, string[] actual",
729+
/* oldAssertion: */ "Assert.Contains(expected, actual);",
730+
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
731+
[DataRow(
732+
/* methodArguments: */ "object expected, List<string> actual",
733+
/* oldAssertion: */ "Assert.Contains(expected, actual);",
734+
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
735+
[DataRow(
736+
/* methodArguments: */ "object expected, DateTime[] actual",
737+
/* oldAssertion: */ "Assert.Contains(expected, actual);",
738+
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
739+
[DataRow(
740+
/* methodArguments: */ "object expected, List<DateTime> actual",
741+
/* oldAssertion: */ "Assert.Contains(expected, actual);",
742+
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
743+
[Implemented]
744+
public void Nunit3_AssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion)
745+
=> Nunit3VerifyFix(methodArguments, oldAssertion, newAssertion);
746+
747+
[DataTestMethod]
748+
[DataRow(
749+
/* methodArguments: */ "object expected, string[] actual",
750+
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
751+
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
752+
[DataRow(
753+
/* methodArguments: */ "object expected, List<string> actual",
754+
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
755+
/* newAssertion: */ "actual.Should().Contain((string)expected);")]
756+
[DataRow(
757+
/* methodArguments: */ "object expected, DateTime[] actual",
758+
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
759+
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
760+
[DataRow(
761+
/* methodArguments: */ "object expected, List<DateTime> actual",
762+
/* oldAssertion: */ "ClassicAssert.Contains(expected, actual);",
763+
/* newAssertion: */ "actual.Should().Contain((DateTime)expected);")]
764+
[Implemented]
765+
public void Nunit4_AssertContains_WithCasting_TestCodeFix(string methodArguments, string oldAssertion, string newAssertion)
766+
=> Nunit4VerifyFix(methodArguments, oldAssertion, newAssertion);
767+
768+
#endregion
769+
670770
private void Nunit3VerifyDiagnostic(string methodArguments, string assertion)
671771
=> VerifyDiagnostic(GenerateCode.Nunit3Assertion(methodArguments, assertion), PackageReference.Nunit_3_14_0);
672772
private void Nunit3VerifyFix(string methodArguments, string oldAssertion, string newAssertion)
673773
=> VerifyFix(GenerateCode.Nunit3Assertion(methodArguments, oldAssertion), GenerateCode.Nunit3Assertion(methodArguments, newAssertion), PackageReference.Nunit_3_14_0);
774+
private void Nunit3VerifyNoFix(string methodArguments, string assertion)
775+
=> VerifyNoFix(GenerateCode.Nunit3Assertion(methodArguments, assertion), PackageReference.Nunit_3_14_0);
674776

675777
private void Nunit4VerifyDiagnostic(string methodArguments, string assertion)
676778
=> VerifyDiagnostic(GenerateCode.Nunit4Assertion(methodArguments, assertion), PackageReference.Nunit_4_0_1);
677779
private void Nunit4VerifyFix(string methodArguments, string oldAssertion, string newAssertion)
678780
=> VerifyFix(GenerateCode.Nunit4Assertion(methodArguments, oldAssertion), GenerateCode.Nunit4Assertion(methodArguments, newAssertion), PackageReference.Nunit_4_0_1);
679-
781+
private void Nunit4VerifyNoFix(string methodArguments, string assertion)
782+
=> VerifyNoFix(GenerateCode.Nunit4Assertion(methodArguments, assertion), PackageReference.Nunit_4_0_1);
680783
private void VerifyDiagnostic(string source, PackageReference nunit)
681784
{
682785
DiagnosticVerifier.VerifyDiagnostic(new DiagnosticVerifierArguments()
@@ -689,7 +792,7 @@ private void VerifyDiagnostic(string source, PackageReference nunit)
689792
Message = AssertAnalyzer.Message,
690793
Locations = new DiagnosticResultLocation[]
691794
{
692-
new("Test0.cs", 15, 13)
795+
new("Test0.cs", 16, 13)
693796
},
694797
Severity = DiagnosticSeverity.Info
695798
})
@@ -706,4 +809,13 @@ private void VerifyFix(string oldSource, string newSource, PackageReference nuni
706809
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, nunit)
707810
);
708811
}
812+
private void VerifyNoFix(string source, PackageReference nunit)
813+
{
814+
DiagnosticVerifier.VerifyNoFix(new CodeFixVerifierArguments()
815+
.WithDiagnosticAnalyzer<AssertAnalyzer>()
816+
.WithCodeFixProvider<NunitCodeFixProvider>()
817+
.WithSources(source)
818+
.WithPackageReferences(PackageReference.FluentAssertions_6_12_0, nunit)
819+
);
820+
}
709821
}

src/FluentAssertions.Analyzers/Tips/NunitCodeFixProvider.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using Microsoft.CodeAnalysis.Operations;
66
using CreateChangedDocument = System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<Microsoft.CodeAnalysis.Document>>;
77
using System;
8+
using FluentAssertions.Analyzers.Utilities;
9+
using Microsoft.CodeAnalysis.Simplification;
810

911
namespace FluentAssertions.Analyzers;
1012

@@ -171,6 +173,30 @@ private CreateChangedDocument TryComputeFixForNunitClassicAssert(IInvocationOper
171173
case "IsNotInstanceOf" when ArgumentsAreTypeOf(invocation, t.Object): // Assert.IsNotInstanceOf<T>(object actual)
172174
case "IsNotInstanceOf" when ArgumentsAreTypeOf(invocation, t.Object, t.String, t.ObjectArray): // Assert.IsNotInstanceOf<T>(object actual, string message, params object[] parms)
173175
return DocumentEditorUtils.RenameGenericMethodToSubjectShouldGenericAssertion(invocation, context, "NotBeOfType", subjectIndex: 0, argumentsToRemove: []);
176+
case "Contains": // Assert.Contains(object expected, ICollection actual)
177+
{
178+
var collectionArgument = invocation.Arguments[1].Value.UnwrapConversion();
179+
if (collectionArgument.Type.ImplementsOrIsInterface(SpecialType.System_Collections_Generic_IEnumerable_T))
180+
{
181+
return async ctx => await DocumentEditorUtils.RewriteExpression(invocation, [
182+
(EditActionContext editActionContext) =>
183+
{
184+
ITypeSymbol elementType = collectionArgument.Type switch
185+
{
186+
INamedTypeSymbol namedType => namedType.TypeArguments[0],
187+
IArrayTypeSymbol arrayType => arrayType.ElementType,
188+
_ => null
189+
};
190+
191+
var argumentToCast = editActionContext.InvocationExpression.ArgumentList.Arguments[0];
192+
var castExpression = editActionContext.Editor.Generator.CastExpression(elementType, argumentToCast.Expression);
193+
editActionContext.Editor.ReplaceNode(argumentToCast.Expression, castExpression.WithAdditionalAnnotations(Simplifier.Annotation));
194+
},
195+
EditAction.SubjectShouldAssertion(argumentIndex: 1, "Contain")
196+
], context, ctx);
197+
}
198+
return null;
199+
}
174200
}
175201
return null;
176202
}

0 commit comments

Comments
 (0)