Skip to content

Commit 29311b3

Browse files
authored
add support for xunit TypeAsserts IsType (#240)
1 parent bf46054 commit 29311b3

File tree

3 files changed

+94
-0
lines changed

3 files changed

+94
-0
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,28 @@ public void AssertIsNotAssignableFrom_TestAnalyzer(string assertion) =>
598598
public void AssertIsNotAssignableFrom_TestCodeFix(string oldAssertion, string newAssertion)
599599
=> VerifyCSharpFix<AssertIsNotAssignableFromCodeFix, AssertIsNotAssignableFromAnalyzer>("string actual, Type expected", oldAssertion, newAssertion);
600600

601+
[DataTestMethod]
602+
[DataRow("Assert.IsType(expected, actual);")]
603+
[DataRow("Assert.IsType(typeof(string), actual);")]
604+
[DataRow("Assert.IsType<string>(actual);")]
605+
[Implemented]
606+
public void AssertIsType_TestAnalyzer(string assertion) =>
607+
VerifyCSharpDiagnostic<AssertIsTypeAnalyzer>("string actual, Type expected", assertion);
608+
609+
[DataTestMethod]
610+
[DataRow(
611+
/* oldAssertion: */ "Assert.IsType(expected, actual);",
612+
/* newAssertion: */ "actual.Should().BeOfType(expected);")]
613+
[DataRow(
614+
/* oldAssertion: */ "Assert.IsType(typeof(string), actual);",
615+
/* newAssertion: */ "actual.Should().BeOfType<string>();")]
616+
[DataRow(
617+
/* oldAssertion: */ "Assert.IsType<string>(actual);",
618+
/* newAssertion: */ "actual.Should().BeOfType<string>();")]
619+
[Implemented]
620+
public void AssertIsType_TestCodeFix(string oldAssertion, string newAssertion)
621+
=> VerifyCSharpFix<AssertIsTypeCodeFix, AssertIsTypeAnalyzer>("string actual, Type expected", oldAssertion, newAssertion);
622+
601623
private void VerifyCSharpDiagnostic<TDiagnosticAnalyzer>(string methodArguments, string assertion) where TDiagnosticAnalyzer : Microsoft.CodeAnalysis.Diagnostics.DiagnosticAnalyzer, new()
602624
{
603625
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
@@ -139,6 +139,7 @@ public static class Xunit
139139
public const string AssertSubset = $"{DiagnosticProperties.IdPrefix}0717";
140140
public const string AssertIsAssignableFrom = $"{DiagnosticProperties.IdPrefix}0718";
141141
public const string AssertIsNotAssignableFrom = $"{DiagnosticProperties.IdPrefix}0719";
142+
public const string AssertIsType = $"{DiagnosticProperties.IdPrefix}0720";
142143
}
143144
}
144145

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System.Collections.Generic;
2+
using System.Collections.Immutable;
3+
using System.Composition;
4+
using System.Linq;
5+
using FluentAssertions.Analyzers.Utilities;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CodeFixes;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
using Microsoft.CodeAnalysis.Diagnostics;
10+
using SF = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
11+
12+
namespace FluentAssertions.Analyzers.Xunit;
13+
14+
[DiagnosticAnalyzer(LanguageNames.CSharp)]
15+
public class AssertIsTypeAnalyzer : XunitAnalyzer
16+
{
17+
public const string DiagnosticId = Constants.Tips.Xunit.AssertIsType;
18+
public const string Category = Constants.Tips.Category;
19+
20+
public const string Message = "Use .Should().BeOfType().";
21+
22+
protected override DiagnosticDescriptor Rule => new(DiagnosticId, Title, Message, Category, DiagnosticSeverity.Info, true);
23+
24+
protected override IEnumerable<FluentAssertionsCSharpSyntaxVisitor> Visitors => new FluentAssertionsCSharpSyntaxVisitor[]
25+
{
26+
new AssertIsTypeGenericTypeSyntaxVisitor(),
27+
new AssertIsTypeTypeSyntaxVisitor()
28+
};
29+
30+
//public static T IsType<T>(object? @object)
31+
public class AssertIsTypeGenericTypeSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
32+
{
33+
public AssertIsTypeGenericTypeSyntaxVisitor() : base(
34+
MemberValidator.HasArguments("IsType", 1)
35+
)
36+
{
37+
}
38+
}
39+
40+
//public static T IsType(Type expectedType, object? @object)
41+
public class AssertIsTypeTypeSyntaxVisitor : FluentAssertionsCSharpSyntaxVisitor
42+
{
43+
public AssertIsTypeTypeSyntaxVisitor() : base(
44+
MemberValidator.HasArguments("IsType", 2)
45+
)
46+
{
47+
}
48+
}
49+
}
50+
51+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AssertIsTypeCodeFix)), Shared]
52+
public class AssertIsTypeCodeFix : XunitCodeFixProvider
53+
{
54+
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(AssertIsTypeAnalyzer.DiagnosticId);
55+
56+
protected override ExpressionSyntax GetNewExpression(
57+
ExpressionSyntax expression,
58+
FluentAssertionsDiagnosticProperties properties)
59+
{
60+
switch (properties.VisitorName)
61+
{
62+
case nameof(AssertIsTypeAnalyzer.AssertIsTypeGenericTypeSyntaxVisitor):
63+
return RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould(expression, "IsType", "BeOfType");
64+
case nameof(AssertIsTypeAnalyzer.AssertIsTypeTypeSyntaxVisitor):
65+
var newExpression = RenameMethodAndReorderActualExpectedAndReplaceWithSubjectShould(expression, "IsType", "BeOfType");
66+
return ReplaceTypeOfArgumentWithGenericTypeIfExists(newExpression, "BeOfType");
67+
default:
68+
throw new System.InvalidOperationException($"Invalid visitor name - {properties.VisitorName}");
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)