Skip to content

Commit 65f5fc5

Browse files
authored
add support for xunit StringAsserts DoesNotMatches (#225)
1 parent 4baf515 commit 65f5fc5

File tree

3 files changed

+88
-0
lines changed

3 files changed

+88
-0
lines changed

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,35 @@ public void AssertStringMatches_Regex_TestAnalyzer(string assertion) =>
396396
public void AssertStringMatches_Regex_TestCodeFix(string oldAssertion, string newAssertion)
397397
=> VerifyCSharpFix<AssertMatchesCodeFix, AssertMatchesAnalyzer>("string actual, Regex expectedRegex", oldAssertion, newAssertion);
398398

399+
[DataTestMethod]
400+
[DataRow("Assert.DoesNotMatch(expectedRegexPattern, actual);")]
401+
[Implemented]
402+
public void AssertStringDoesNotMatch_String_TestAnalyzer(string assertion) =>
403+
VerifyCSharpDiagnostic<AssertDoesNotMatchAnalyzer>("string actual, string expectedRegexPattern", assertion);
404+
405+
[DataTestMethod]
406+
[DataRow(
407+
/* oldAssertion: */ "Assert.DoesNotMatch(expectedRegexPattern, actual);",
408+
/* newAssertion: */ "actual.Should().NotMatchRegex(expectedRegexPattern);")]
409+
[Implemented]
410+
public void AssertStringDoesNotMatch_String_TestCodeFix(string oldAssertion, string newAssertion)
411+
=> VerifyCSharpFix<AssertDoesNotMatchCodeFix, AssertDoesNotMatchAnalyzer>("string actual, string expectedRegexPattern", oldAssertion, newAssertion);
412+
413+
[DataTestMethod]
414+
[DataRow("Assert.DoesNotMatch(expectedRegex, actual);")]
415+
[Implemented]
416+
public void AssertStringDoesNotMatch_Regex_TestAnalyzer(string assertion) =>
417+
VerifyCSharpDiagnostic<AssertDoesNotMatchAnalyzer>("string actual, Regex expectedRegex", assertion);
418+
419+
[DataTestMethod]
420+
[DataRow(
421+
/* oldAssertion: */ "Assert.DoesNotMatch(expectedRegex, actual);",
422+
/* newAssertion: */ "actual.Should().NotMatchRegex(expectedRegex);")]
423+
[Implemented]
424+
public void AssertStringDoesNotMatch_Regex_TestCodeFix(string oldAssertion, string newAssertion)
425+
=> VerifyCSharpFix<AssertDoesNotMatchCodeFix, AssertDoesNotMatchAnalyzer>("string actual, Regex expectedRegex", oldAssertion, newAssertion);
426+
427+
399428
private void VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string methodArguments, string assertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
400429
{
401430
var source = GenerateCode.XunitAssertion(methodArguments, assertion);

src/FluentAssertions.Analyzers/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ public static class Xunit
132132
public const string AssertContains = $"{DiagnosticProperties.IdPrefix}0710";
133133
public const string AssertDoesNotContain = $"{DiagnosticProperties.IdPrefix}0711";
134134
public const string AssertMatches = $"{DiagnosticProperties.IdPrefix}0712";
135+
public const string AssertDoesNotMatch = $"{DiagnosticProperties.IdPrefix}0713";
135136
}
136137
}
137138

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Collections.Generic;
2+
using System.Collections.Immutable;
3+
using System.Composition;
4+
using FluentAssertions.Analyzers.Utilities;
5+
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.CodeFixes;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
8+
using Microsoft.CodeAnalysis.Diagnostics;
9+
10+
namespace FluentAssertions.Analyzers.Xunit;
11+
12+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
13+
public class AssertDoesNotMatchAnalyzer : XunitAnalyzer
14+
{
15+
public const string DiagnosticId = Constants.Tips.Xunit.AssertDoesNotMatch;
16+
public const string Category = Constants.Tips.Category;
17+
18+
public const string Message = "Use .Should().NotMatchRegex()";
19+
20+
protected override DiagnosticDescriptor Rule => new(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
21+
22+
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors => new FluentAssertionsCSharpSyntaxVisitor[]
23+
{
24+
new AssertDoesNotMatchStringSyntaxVisitor()
25+
};
26+
27+
//public static void DoesNotMatch(string expectedRegexPattern, string? actualString)
28+
//public static void DoesNotMatch(Regex expectedRegex, string? actualString)
29+
public class AssertDoesNotMatchStringSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
30+
{
31+
public AssertDoesNotMatchStringSyntaxVisitor() : base(
32+
MemberValidator.ArgumentsMatch("DoesNotMatch",
33+
ArgumentValidator.IsAnyType(TypeSelector.GetStringType, TypeSelector.GetRegexType),
34+
ArgumentValidator.IsType(TypeSelector.GetStringType))
35+
)
36+
{
37+
}
38+
}
39+
}
40+
41+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssertDoesNotMatchCodeFix)), Shared]
42+
public class AssertDoesNotMatchCodeFix : XunitCodeFixProvider
43+
{
44+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertDoesNotMatchAnalyzer.DiagnosticId);
45+
46+
protected override ExpressionSyntax GetNewExpression(
47+
ExpressionSyntax expression,
48+
FluentAssertionsDiagnosticProperties properties)
49+
{
50+
switch (properties.VisitorName)
51+
{
52+
case nameof(AssertDoesNotMatchAnalyzer.AssertDoesNotMatchStringSyntaxVisitor):
53+
return RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould(expression, "DoesNotMatch", "NotMatchRegex");
54+
default:
55+
throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}");
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)