From 6c8bfa692d81379f4762bc38e300ff11e8ca104d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Fri, 15 Mar 2024 18:23:46 +0100 Subject: [PATCH] MSTEST0020: Prefer ctors over TestInitialize methods --- .../AnalyzerReleases.Unshipped.md | 1 + .../MSTest.Analyzers/Helpers/DiagnosticIds.cs | 1 + ...erConstructorOverTestInitializeAnalyzer.cs | 56 +++++++++++ .../MSTest.Analyzers/Resources.Designer.cs | 18 ++++ src/Analyzers/MSTest.Analyzers/Resources.resx | 6 ++ .../MSTest.Analyzers/xlf/Resources.cs.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.de.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.es.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.fr.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.it.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.ja.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.ko.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.pl.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.pt-BR.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.ru.xlf | 10 ++ .../MSTest.Analyzers/xlf/Resources.tr.xlf | 10 ++ .../xlf/Resources.zh-Hans.xlf | 10 ++ .../xlf/Resources.zh-Hant.xlf | 10 ++ ...structorOverTestInitializeAnalyzerTests.cs | 92 +++++++++++++++++++ .../testsbaseline.txt | 4 + 20 files changed, 308 insertions(+) create mode 100644 src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs create mode 100644 test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index 88f8b07442..7b7ac8230f 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -5,3 +5,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- MSTEST0017 | Usage | Info | AssertionArgsShouldBePassedInCorrectOrder, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0017) +MSTEST0020 | Design | Disabled | PreferConstructorOverTestInitializeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0020) diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 31b3fa1699..3df97939ec 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -22,4 +22,5 @@ internal static class DiagnosticIds public const string TestMethodShouldNotBeIgnoredRuleId = "MSTEST0015"; public const string TestClassShouldHaveTestMethodRuleId = "MSTEST0016"; public const string AssertionArgsShouldBePassedInCorrectOrderRuleId = "MSTEST0017"; + public const string PreferConstructorOverTestInitializeRuleId = "MSTEST0020"; } diff --git a/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs new file mode 100644 index 0000000000..ba1e86a8b8 --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/PreferConstructorOverTestInitializeAnalyzer.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Immutable; + +using Analyzer.Utilities.Extensions; + +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class PreferConstructorOverTestInitializeAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.PreferConstructorOverTestInitializeTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.PreferConstructorOverTestInitializeMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.PreferConstructorOverTestInitializeRuleId, + Title, + MessageFormat, + null, + Category.Design, + DiagnosticSeverity.Info, + isEnabledByDefault: false); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + if (context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute, out var testInitAttributeSymbol)) + { + context.RegisterSymbolAction(context => AnalyzeSymbol(context, testInitAttributeSymbol), SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testInitAttributeSymbol) + { + IMethodSymbol methodSymbol = (IMethodSymbol)context.Symbol; + + if (methodSymbol.IsTestInitializeMethod(testInitAttributeSymbol) && methodSymbol.ReturnsVoid) + { + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(Rule)); + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs index 27f32bd09d..81422fe2b9 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs @@ -551,6 +551,24 @@ internal static string DataRowShouldBeValidTitle { } } + /// + /// Looks up a localized string similar to Prefer constructors over TestInitialize methods. + /// + internal static string PreferConstructorOverTestInitializeMessageFormat { + get { + return ResourceManager.GetString("PreferConstructorOverTestInitializeMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Prefer constructors over TestInitialize methods. + /// + internal static string PreferConstructorOverTestInitializeTitle { + get { + return ResourceManager.GetString("PreferConstructorOverTestInitializeTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to It's considered a good practice to have only test classes marked public in a test project.. /// diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index d5f00cf281..c200a90ff7 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -301,6 +301,12 @@ DataRow should be valid + + Prefer constructors over TestInitialize methods + + + Prefer constructors over TestInitialize methods + It's considered a good practice to have only test classes marked public in a test project. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index 5a520b271a..0e257cdf65 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -323,6 +323,16 @@ Argument DataRow by měl být platný + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. Osvědčeným postupem je označit jako veřejné v testovacím projektu jen testovací třídy. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index 8d1d76d43d..c86e331bc3 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -319,6 +319,16 @@ DataRow muss gültig sein. + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. Es wird als bewährte Methode angesehen, nur Testklassen in einem Testprojekt als öffentlich gekennzeichnet zu lassen. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index b17db60bb7..42994f7f45 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -319,6 +319,16 @@ DataRow debe ser válido + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. Se considera una buena práctica tener solo clases de prueba marcadas como públicas en un proyecto de prueba. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index d7cdef25ee..f9fe3b3030 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -319,6 +319,16 @@ DataRow doit être valide + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. C’est considéré comme une bonne pratique d’avoir uniquement des classes de test marquées comme publiques dans un projet de test. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index f5abdb602d..d1ef4de4c0 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -319,6 +319,16 @@ DataRow deve essere valido + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. È consigliabile che solo le classi di test siano contrassegnate come pubbliche in un progetto di test. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index 374e9ddb80..e5139e19ca 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -319,6 +319,16 @@ DataRow は有効である必要があります + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. テスト プロジェクトでは、テスト クラスのみをパブリックとしてマークすることをお勧めします。 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 8aed9a7cb2..978ffc0d92 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -319,6 +319,16 @@ DataRow는 유효해야 함 + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. 테스트 프로젝트에서 공용으로 표시된 테스트 클래스만 사용하는 것이 좋습니다. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 27c91051cf..ecf259e9a5 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -319,6 +319,16 @@ Element DataRow powinien być prawidłowy + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. Uważa się, że dobrą praktyką jest oznaczanie tylko klas testowych jako publicznych w projekcie testowym. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index cb93f04d6f..4e3e232845 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -319,6 +319,16 @@ DataRow deve ser válido + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. É considerado uma boa prática ter somente classes de teste marcadas como públicas em um projeto de teste. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index 9ccb7ea34b..2ab13c5f19 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -319,6 +319,16 @@ Значение DataRow должно быть допустимым + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. Рекомендуется использовать только тестовые классы, помеченные как общедоступные в тестовом проекте. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index 74fd66cf06..600e772ba4 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -319,6 +319,16 @@ DataRow geçerli olmalıdır + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. Test projesinde yalnızca public olarak işaretlenmiş test sınıflarının olması iyi bir uygulama olarak kabul edilir. diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index 1fbe5b6582..bbb885344f 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -319,6 +319,16 @@ DataRow 应有效 + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. 在测试项目中仅将测试类标记为“公开”是一种不错的做法。 diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index def0f476fe..0c6971faa5 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -319,6 +319,16 @@ DataRow 應該有效 + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + + + Prefer constructors over TestInitialize methods + Prefer constructors over TestInitialize methods + + It's considered a good practice to have only test classes marked public in a test project. 在測試專案中僅將測試類別標記為公開被認為是一種很好的做法。 diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs new file mode 100644 index 0000000000..9a1301711f --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/PreferConstructorOverTestInitializeAnalyzerTests.cs @@ -0,0 +1,92 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Testing.Framework; +using Microsoft.Testing.TestInfrastructure; + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.PreferConstructorOverTestInitializeAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestGroup] +public sealed class PreferConstructorOverTestInitializeAnalyzerTests(ITestExecutionContext testExecutionContext) : TestBase(testExecutionContext) +{ + public async Task WhenTestClassHasCtor_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public MyTestClass() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestClassHasTestInitialize_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void [|MyTestInit|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestClassHasTestInitializeAsync_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public Task MyTestInit() + { + return Task.CompletedTask; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + public async Task WhenTestClassHasTestInitializeAndCtor_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public MyTestClass() + { + } + + [TestInitialize] + public void [|MyTestInit|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +} diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt b/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt index 956212bab1..1294274a20 100644 --- a/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/testsbaseline.txt @@ -85,6 +85,10 @@ MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTes MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasArrayArgument_NoDiagnostic() MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsCorrectlyDefinedWithThreeArgumentsAndMethodHasParamsArgument_NoDiagnostic() MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.DataRowShouldBeValidAnalyzerTests.WhenDataRowIsNotSetOnATestMethod_Diagnostic() +MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasCtor_NoDiagnostic() +MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasTestInitialize_Diagnostic() +MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasTestInitializeAndCtor_Diagnostic() +MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PreferConstructorOverTestInitializeAnalyzerTests.WhenTestClassHasTestInitializeAsync_NoDiagnostic() MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PublicClassShouldBeTestClassAnalyzerTests.WhenTypeIsNotPublicAndNotTestClass_NoDiagnostic() MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.PublicClassShouldBeTestClassAnalyzerTests.WhenTypeIsPublicAndNotTestClass_Diagnostic() MSTest.Analyzers.UnitTests.MSTest.Analyzers.Test.TestClassShouldBeValidAnalyzerTests.WhenClassIsGeneric_NoDiagnostic()