Skip to content

Commit 757fa0f

Browse files
author
Corniel Nobel
committed
Move logic to UnitTestHelper.
1 parent 18ce738 commit 757fa0f

File tree

2 files changed

+119
-133
lines changed

2 files changed

+119
-133
lines changed

analyzers/src/SonarAnalyzer.CSharp/Rules/TestMethodShouldContainAssertion.cs

+5-27
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ protected override void Initialize(SonarAnalysisContext context) =>
6363
var methodDeclaration = MethodDeclarationFactory.Create(c.Node);
6464
if (!methodDeclaration.Identifier.IsMissing
6565
&& methodDeclaration.HasImplementation
66-
&& c.SemanticModel.GetDeclaredSymbol(c.Node) is IMethodSymbol methodSymbol
67-
&& IsTestMethod(methodSymbol, methodDeclaration.IsLocal)
68-
&& !methodSymbol.HasExpectedExceptionAttribute()
69-
&& !methodSymbol.HasAssertionInAttribute()
70-
&& !IsTestIgnored(methodSymbol)
66+
&& c.SemanticModel.GetDeclaredSymbol(c.Node) is IMethodSymbol method
67+
&& method.IsTestMethod()
68+
&& !method.HasExpectedExceptionAttribute()
69+
&& !method.HasAssertionInAttribute()
70+
&& !method.IsIgnoredTestMethod()
7171
&& !ContainsAssertion(c.Node, c.SemanticModel, new HashSet<IMethodSymbol>(), 0))
7272
{
7373
c.ReportIssue(Diagnostic.Create(Rule, methodDeclaration.Identifier.GetLocation()));
@@ -76,13 +76,6 @@ protected override void Initialize(SonarAnalysisContext context) =>
7676
SyntaxKind.MethodDeclaration,
7777
SyntaxKindEx.LocalFunctionStatement);
7878

79-
// only xUnit allows local functions to be test methods.
80-
private static bool IsTestMethod(IMethodSymbol symbol, bool isLocalFunction) =>
81-
isLocalFunction ? IsXunitTestMethod(symbol) : symbol.IsTestMethod();
82-
83-
private static bool IsXunitTestMethod(IMethodSymbol methodSymbol) =>
84-
methodSymbol.AnyAttributeDerivesFromAny(UnitTestHelper.KnownTestMethodAttributesOfxUnit);
85-
8679
private static bool ContainsAssertion(SyntaxNode methodDeclaration, SemanticModel previousSemanticModel, ISet<IMethodSymbol> visitedSymbols, int level)
8780
{
8881
var currentSemanticModel = methodDeclaration.EnsureCorrectSemanticModelOrDefault(previousSemanticModel);
@@ -125,21 +118,6 @@ private static bool ContainsAssertion(SyntaxNode methodDeclaration, SemanticMode
125118
return false;
126119
}
127120

128-
private static bool IsTestIgnored(IMethodSymbol method)
129-
{
130-
if (method.IsMsTestOrNUnitTestIgnored())
131-
{
132-
return true;
133-
}
134-
135-
// Checking whether an Xunit test is ignore or not needs to be done at the syntax level i.e. language-specific
136-
var factAttributeSyntax = method.FindXUnitTestAttribute()
137-
?.ApplicationSyntaxReference.GetSyntax() as AttributeSyntax;
138-
139-
return factAttributeSyntax?.ArgumentList != null
140-
&& factAttributeSyntax.ArgumentList.Arguments.Any(x => x.NameEquals.Name.Identifier.ValueText == "Skip");
141-
}
142-
143121
private static bool IsAssertion(InvocationExpressionSyntax invocation) =>
144122
invocation.Expression
145123
.ToString()

analyzers/src/SonarAnalyzer.Common/Helpers/UnitTestHelper.cs

+114-106
Original file line numberDiff line numberDiff line change
@@ -18,111 +18,119 @@
1818
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
1919
*/
2020

21-
namespace SonarAnalyzer.Helpers
21+
namespace SonarAnalyzer.Helpers;
22+
23+
// Note: useful comparison of the differing syntax across unit test frameworks at https://xunit.net/docs/comparisons
24+
internal static class UnitTestHelper
2225
{
23-
// Note: useful comparison of the differing syntax across unit test frameworks at https://xunit.net/docs/comparisons
24-
internal static class UnitTestHelper
25-
{
26-
public static readonly ImmutableArray<KnownType> KnownTestMethodAttributesOfMSTest = ImmutableArray.Create(
27-
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_TestMethodAttribute,
28-
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_DataTestMethodAttribute);
29-
30-
public static readonly ImmutableArray<KnownType> KnownTestMethodAttributesOfNUnit = ImmutableArray.Create(
31-
KnownType.NUnit_Framework_TestAttribute,
32-
KnownType.NUnit_Framework_TestCaseAttribute,
33-
KnownType.NUnit_Framework_TestCaseSourceAttribute,
34-
KnownType.NUnit_Framework_TheoryAttribute,
35-
KnownType.NUnit_Framework_ITestBuilderInterface);
36-
37-
public static readonly ImmutableArray<KnownType> KnownTestMethodAttributesOfxUnit = ImmutableArray.Create(
38-
KnownType.Xunit_TheoryAttribute,
39-
KnownType.LegacyXunit_TheoryAttribute,
40-
// In order for the FindFirstTestMethodType to work, FactAttribute should go last as the Theory attribute derives from it.
41-
KnownType.Xunit_FactAttribute);
42-
43-
public static readonly ImmutableArray<KnownType> KnownExpectedExceptionAttributes = ImmutableArray.Create(
44-
// Note: XUnit doesn't have a exception attribute
45-
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_ExpectedExceptionAttribute,
46-
KnownType.NUnit_Framework_ExpectedExceptionAttribute);
47-
48-
public static readonly ImmutableArray<KnownType> KnownIgnoreAttributes = ImmutableArray.Create(
49-
// Note: XUnit doesn't have a separate "Ignore" attribute. It has a "Skip" parameter
50-
// on the test attribute
51-
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_IgnoreAttribute,
52-
KnownType.NUnit_Framework_IgnoreAttribute);
53-
54-
/// <summary>
55-
/// List of partial names that are assumed to indicate an assertion method.
56-
/// </summary>
57-
public static readonly ImmutableArray<string> KnownAssertionMethodParts = ImmutableArray.Create(
58-
"ASSERT",
59-
"CHECK",
60-
"EXPECT",
61-
"MUST",
62-
"SHOULD",
63-
"VERIFY",
64-
"VALIDATE");
65-
66-
private static readonly ImmutableArray<KnownType> KnownTestMethodAttributes = ImmutableArray.Create(
67-
KnownTestMethodAttributesOfMSTest
68-
.Concat(KnownTestMethodAttributesOfNUnit)
69-
.Concat(KnownTestMethodAttributesOfxUnit)
70-
.ToArray());
71-
72-
private static readonly ImmutableArray<KnownType> KnownTestClassAttributes = ImmutableArray.Create(
73-
// xUnit does not have have attributes to identity test classes
74-
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_TestClassAttribute,
75-
KnownType.NUnit_Framework_TestFixtureAttribute);
76-
77-
private static readonly ImmutableArray<KnownType> NoExpectedResultTestMethodReturnTypes = ImmutableArray.Create(
78-
KnownType.Void,
79-
KnownType.System_Threading_Tasks_Task);
80-
81-
/// <summary>
82-
/// Returns whether the class has an attribute that marks the class
83-
/// as an MSTest or NUnit test class (xUnit doesn't have any such attributes).
84-
/// </summary>
85-
public static bool IsTestClass(this INamedTypeSymbol classSymbol) =>
86-
classSymbol.AnyAttributeDerivesFromAny(KnownTestClassAttributes);
87-
88-
public static bool IsTestMethod(this IMethodSymbol method) =>
89-
method.AnyAttributeDerivesFromOrImplementsAny(KnownTestMethodAttributes);
90-
91-
public static bool HasExpectedExceptionAttribute(this IMethodSymbol method) =>
92-
method.GetAttributes().Any(a =>
93-
a.AttributeClass.IsAny(KnownExpectedExceptionAttributes)
94-
|| a.AttributeClass.DerivesFrom(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_ExpectedExceptionBaseAttribute));
95-
96-
public static bool HasAssertionInAttribute(this IMethodSymbol method) =>
97-
!NoExpectedResultTestMethodReturnTypes.Any(method.ReturnType.Is)
98-
&& method.GetAttributes().Any(IsAnyTestCaseAttributeWithExpectedResult);
99-
100-
public static bool IsMsTestOrNUnitTestIgnored(this IMethodSymbol method) =>
101-
method.GetAttributes().Any(a => a.AttributeClass.IsAny(KnownIgnoreAttributes));
102-
103-
public static AttributeData FindXUnitTestAttribute(this IMethodSymbol method) =>
104-
method.GetAttributes().FirstOrDefault(a =>
105-
a.AttributeClass.Is(KnownType.Xunit_FactAttribute)
106-
|| a.AttributeClass.Is(KnownType.Xunit_TheoryAttribute)
107-
|| a.AttributeClass.Is(KnownType.LegacyXunit_TheoryAttribute));
108-
109-
/// <summary>
110-
/// Returns the <see cref="KnownType"/> that indicates the type of the test method or
111-
/// null if the method is not decorated with a known type.
112-
/// </summary>
113-
/// <remarks>We assume that a test is only marked with a single test attribute e.g.
114-
/// not both [Fact] and [Theory]. If there are multiple attributes only one will be
115-
/// returned.</remarks>
116-
public static KnownType FindFirstTestMethodType(this IMethodSymbol method) =>
117-
KnownTestMethodAttributes.FirstOrDefault(known =>
118-
method.GetAttributes().Any(att => att.AttributeClass.DerivesFrom(known)));
119-
120-
private static bool IsAnyTestCaseAttributeWithExpectedResult(AttributeData a) =>
121-
IsTestAttributeWithExpectedResult(a)
122-
|| a.AttributeClass.Is(KnownType.NUnit_Framework_TestCaseSourceAttribute);
123-
124-
private static bool IsTestAttributeWithExpectedResult(AttributeData a) =>
125-
a.AttributeClass.IsAny(KnownType.NUnit_Framework_TestCaseAttribute, KnownType.NUnit_Framework_TestAttribute)
126-
&& a.NamedArguments.Any(arg => arg.Key == "ExpectedResult");
127-
}
26+
public static readonly ImmutableArray<KnownType> KnownTestMethodAttributesOfMSTest = ImmutableArray.Create(
27+
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_TestMethodAttribute,
28+
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_DataTestMethodAttribute);
29+
30+
public static readonly ImmutableArray<KnownType> KnownTestMethodAttributesOfNUnit = ImmutableArray.Create(
31+
KnownType.NUnit_Framework_TestAttribute,
32+
KnownType.NUnit_Framework_TestCaseAttribute,
33+
KnownType.NUnit_Framework_TestCaseSourceAttribute,
34+
KnownType.NUnit_Framework_TheoryAttribute,
35+
KnownType.NUnit_Framework_ITestBuilderInterface);
36+
37+
public static readonly ImmutableArray<KnownType> KnownTestMethodAttributesOfxUnit = ImmutableArray.Create(
38+
KnownType.Xunit_TheoryAttribute,
39+
KnownType.LegacyXunit_TheoryAttribute,
40+
// In order for the FindFirstTestMethodType to work, FactAttribute should go last as the Theory attribute derives from it.
41+
KnownType.Xunit_FactAttribute);
42+
43+
public static readonly ImmutableArray<KnownType> KnownExpectedExceptionAttributes = ImmutableArray.Create(
44+
// Note: XUnit doesn't have a exception attribute
45+
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_ExpectedExceptionAttribute,
46+
KnownType.NUnit_Framework_ExpectedExceptionAttribute);
47+
48+
public static readonly ImmutableArray<KnownType> KnownIgnoreAttributes = ImmutableArray.Create(
49+
// Note: XUnit doesn't have a separate "Ignore" attribute. It has a "Skip" parameter
50+
// on the test attribute
51+
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_IgnoreAttribute,
52+
KnownType.NUnit_Framework_IgnoreAttribute);
53+
54+
/// <summary>
55+
/// List of partial names that are assumed to indicate an assertion method.
56+
/// </summary>
57+
public static readonly ImmutableArray<string> KnownAssertionMethodParts = ImmutableArray.Create(
58+
"ASSERT",
59+
"CHECK",
60+
"EXPECT",
61+
"MUST",
62+
"SHOULD",
63+
"VERIFY",
64+
"VALIDATE");
65+
66+
private static readonly ImmutableArray<KnownType> KnownTestMethodAttributes = ImmutableArray.Create(
67+
KnownTestMethodAttributesOfMSTest
68+
.Concat(KnownTestMethodAttributesOfNUnit)
69+
.Concat(KnownTestMethodAttributesOfxUnit)
70+
.ToArray());
71+
72+
private static readonly ImmutableArray<KnownType> KnownTestClassAttributes = ImmutableArray.Create(
73+
// xUnit does not have have attributes to identity test classes
74+
KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_TestClassAttribute,
75+
KnownType.NUnit_Framework_TestFixtureAttribute);
76+
77+
private static readonly ImmutableArray<KnownType> NoExpectedResultTestMethodReturnTypes = ImmutableArray.Create(
78+
KnownType.Void,
79+
KnownType.System_Threading_Tasks_Task);
80+
81+
/// <summary>
82+
/// Returns whether the class has an attribute that marks the class
83+
/// as an MSTest or NUnit test class (xUnit doesn't have any such attributes).
84+
/// </summary>
85+
public static bool IsTestClass(this INamedTypeSymbol classSymbol) =>
86+
classSymbol.AnyAttributeDerivesFromAny(KnownTestClassAttributes);
87+
88+
public static bool IsTestMethod(this IMethodSymbol method) =>
89+
method.MethodKind.HasFlag(MethodKindEx.LocalFunction)
90+
? method.IsXunitTestMethod()
91+
: method.AnyAttributeDerivesFromOrImplementsAny(KnownTestMethodAttributes);
92+
93+
public static bool IsIgnoredTestMethod(this IMethodSymbol method) =>
94+
method.IsMsTestOrNUnitTestIgnored()
95+
|| method.FindXUnitTestAttribute().NamedArguments.Any(arg => arg.Key == "Skip");
96+
97+
public static bool HasExpectedExceptionAttribute(this IMethodSymbol method) =>
98+
method.GetAttributes().Any(a =>
99+
a.AttributeClass.IsAny(KnownExpectedExceptionAttributes)
100+
|| a.AttributeClass.DerivesFrom(KnownType.Microsoft_VisualStudio_TestTools_UnitTesting_ExpectedExceptionBaseAttribute));
101+
102+
public static bool HasAssertionInAttribute(this IMethodSymbol method) =>
103+
!NoExpectedResultTestMethodReturnTypes.Any(method.ReturnType.Is)
104+
&& method.GetAttributes().Any(IsAnyTestCaseAttributeWithExpectedResult);
105+
106+
public static AttributeData FindXUnitTestAttribute(this IMethodSymbol method) =>
107+
method.GetAttributes().FirstOrDefault(a =>
108+
a.AttributeClass.Is(KnownType.Xunit_FactAttribute)
109+
|| a.AttributeClass.Is(KnownType.Xunit_TheoryAttribute)
110+
|| a.AttributeClass.Is(KnownType.LegacyXunit_TheoryAttribute));
111+
112+
/// <summary>
113+
/// Returns the <see cref="KnownType"/> that indicates the type of the test method or
114+
/// null if the method is not decorated with a known type.
115+
/// </summary>
116+
/// <remarks>We assume that a test is only marked with a single test attribute e.g.
117+
/// not both [Fact] and [Theory]. If there are multiple attributes only one will be
118+
/// returned.</remarks>
119+
public static KnownType FindFirstTestMethodType(this IMethodSymbol method) =>
120+
KnownTestMethodAttributes.FirstOrDefault(known =>
121+
method.GetAttributes().Any(att => att.AttributeClass.DerivesFrom(known)));
122+
123+
private static bool IsAnyTestCaseAttributeWithExpectedResult(AttributeData a) =>
124+
IsTestAttributeWithExpectedResult(a)
125+
|| a.AttributeClass.Is(KnownType.NUnit_Framework_TestCaseSourceAttribute);
126+
127+
private static bool IsTestAttributeWithExpectedResult(AttributeData a) =>
128+
a.AttributeClass.IsAny(KnownType.NUnit_Framework_TestCaseAttribute, KnownType.NUnit_Framework_TestAttribute)
129+
&& a.NamedArguments.Any(arg => arg.Key == "ExpectedResult");
130+
131+
private static bool IsXunitTestMethod(this IMethodSymbol methodSymbol) =>
132+
methodSymbol.AnyAttributeDerivesFromAny(KnownTestMethodAttributesOfxUnit);
133+
134+
private static bool IsMsTestOrNUnitTestIgnored(this IMethodSymbol method) =>
135+
method.GetAttributes().Any(a => a.AttributeClass.IsAny(KnownIgnoreAttributes));
128136
}

0 commit comments

Comments
 (0)