From 0a42a3999eee0931e482e6f293bab58a29c7f24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9rald=20Barr=C3=A9?= Date: Sun, 13 Sep 2026 21:31:42 -0400 Subject: [PATCH] Consider inherited test methods when detecting test classes (CA1515 suppressor, xUnit1056) IsTestClassNonStrict only looked at the type's own members, so a concrete class whose test methods were all inherited from a base class was not recognized as a test class. The CA1515 suppressor therefore left the warning on the class xUnit actually runs. --- .../X1056_TypeMustHaveSinglePublicConstructorTests.cs | 8 ++++++++ .../CA1515_MakeTypesInternalSuppressorTests.cs | 8 ++++++++ src/xunit.analyzers/Utility/CodeAnalysisExtensions.cs | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/xunit.analyzers.tests/Analyzers/X1000/X1056_TypeMustHaveSinglePublicConstructorTests.cs b/src/xunit.analyzers.tests/Analyzers/X1000/X1056_TypeMustHaveSinglePublicConstructorTests.cs index d2d38d89..602685b9 100644 --- a/src/xunit.analyzers.tests/Analyzers/X1000/X1056_TypeMustHaveSinglePublicConstructorTests.cs +++ b/src/xunit.analyzers.tests/Analyzers/X1000/X1056_TypeMustHaveSinglePublicConstructorTests.cs @@ -54,6 +54,13 @@ static TestClass_DualConstructor() { } [Fact] public void TestMethod() { } } + public class InheritedTestClass_DefaultConstructor : AbstractTestClass { } + + public class {|#1:InheritedTestClass_DualConstructor|} : AbstractTestClass { + public InheritedTestClass_DualConstructor() { } + public InheritedTestClass_DualConstructor(int x) { } + } + // Fixture constructors public class Fixture_DefaultConstructor { } @@ -101,6 +108,7 @@ public class {|#11:CollectionFixtureContainer|} : """; var expected = new[] { Verify.Diagnostic().WithLocation(0).WithArguments("Test class", "TestClass_DualConstructor"), + Verify.Diagnostic().WithLocation(1).WithArguments("Test class", "InheritedTestClass_DualConstructor"), Verify.Diagnostic().WithLocation(10).WithArguments("Fixture", "Fixture_DualConstructor"), Verify.Diagnostic().WithLocation(10).WithArguments("Fixture", "Fixture_Abstract"), diff --git a/src/xunit.analyzers.tests/Suppressors/CA1515_MakeTypesInternalSuppressorTests.cs b/src/xunit.analyzers.tests/Suppressors/CA1515_MakeTypesInternalSuppressorTests.cs index 126614f9..86d58886 100644 --- a/src/xunit.analyzers.tests/Suppressors/CA1515_MakeTypesInternalSuppressorTests.cs +++ b/src/xunit.analyzers.tests/Suppressors/CA1515_MakeTypesInternalSuppressorTests.cs @@ -24,10 +24,18 @@ [Fact] public void TestMethod() { } public class {|#1:Theory_TestClass_Suppresses|} { [Theory] public void TestMethod() { } } + + public abstract class {|#2:BaseTestClass_Suppresses|} { + [Fact] public void TestMethod() { } + } + + public class {|#3:InheritedTestClass_Suppresses|} : BaseTestClass_Suppresses { } """; var expected = new[] { new DiagnosticResult("CA1515", DiagnosticSeverity.Warning).WithLocation(0).WithIsSuppressed(true), new DiagnosticResult("CA1515", DiagnosticSeverity.Warning).WithLocation(1).WithIsSuppressed(true), + new DiagnosticResult("CA1515", DiagnosticSeverity.Warning).WithLocation(2).WithIsSuppressed(true), + new DiagnosticResult("CA1515", DiagnosticSeverity.Warning).WithLocation(3).WithIsSuppressed(true), }; await Verify.VerifySuppressor(code, CodeAnalysisNetAnalyzers.CA1515(), expected); diff --git a/src/xunit.analyzers/Utility/CodeAnalysisExtensions.cs b/src/xunit.analyzers/Utility/CodeAnalysisExtensions.cs index 90e4fcdc..066738a2 100644 --- a/src/xunit.analyzers/Utility/CodeAnalysisExtensions.cs +++ b/src/xunit.analyzers/Utility/CodeAnalysisExtensions.cs @@ -266,7 +266,7 @@ static bool IsTestClassNonStrict( return type - .GetMembers() + .GetInheritedAndOwnMembers() .OfType() .Any(method => method