diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index db7d4e4da2..47b4e8c12a 100644 --- a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md +++ b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md @@ -6,3 +6,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|------- MSTEST0058 | Usage | Info | AvoidAssertsInCatchBlocksAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0058) +MSTEST0059 | Usage | Warning | DuplicateTestMethodAttributeAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0059) diff --git a/src/Analyzers/MSTest.Analyzers/DuplicateTestMethodAttributeAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/DuplicateTestMethodAttributeAnalyzer.cs new file mode 100644 index 0000000000..a25f7faf5f --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/DuplicateTestMethodAttributeAnalyzer.cs @@ -0,0 +1,69 @@ +// 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; + +/// +/// MSTEST0059: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class DuplicateTestMethodAttributeAnalyzer : DiagnosticAnalyzer +{ + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.DuplicateTestMethodAttributeRuleId, + new LocalizableResourceString(nameof(Resources.DuplicateTestMethodAttributeTitle), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.DuplicateTestMethodAttributeMessageFormat), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.DuplicateTestMethodAttributeDescription), Resources.ResourceManager, typeof(Resources)), + Category.Usage, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + 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.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol)) + { + context.RegisterSymbolAction( + context => AnalyzeSymbol(context, testMethodAttributeSymbol), + SymbolKind.Method); + } + }); + } + + private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbol testMethodAttributeSymbol) + { + var methodSymbol = (IMethodSymbol)context.Symbol; + + int testMethodAttributeCount = 0; + foreach (AttributeData attribute in methodSymbol.GetAttributes()) + { + if (attribute.AttributeClass is not null && attribute.AttributeClass.Inherits(testMethodAttributeSymbol)) + { + testMethodAttributeCount++; + if (testMethodAttributeCount > 1) + { + // Report diagnostic on the method itself + context.ReportDiagnostic(methodSymbol.CreateDiagnostic(Rule, methodSymbol.Name)); + return; + } + } + } + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 15b809366e..4bd0292609 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -63,4 +63,5 @@ internal static class DiagnosticIds public const string TestMethodAttributeShouldSetDisplayNameCorrectlyRuleId = "MSTEST0056"; public const string TestMethodAttributeShouldPropagateSourceInformationRuleId = "MSTEST0057"; public const string AvoidAssertsInCatchBlocksRuleId = "MSTEST0058"; + public const string DuplicateTestMethodAttributeRuleId = "MSTEST0059"; } diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index a8d5123404..6f6764692f 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -699,4 +699,13 @@ The type declaring these methods should also respect the following rules: Using asserts in catch blocks is problematic because the test will pass even if no exception is thrown and the catch block is never executed. Use 'Assert.Throws', 'Assert.ThrowsExactly', 'Assert.ThrowsAsync' or 'Assert.ThrowsExactlyAsync' to verify that an exception is thrown, and then make additional assertions on the caught exception without using the try-catch block. + + Avoid duplicate test method attributes + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index 1a543104f9..23320dac4d 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -1014,6 +1014,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Používání kontrolních výrazů v blocích catch je problematické, protože test projde, i když se nevyvolá žádná výjimka a blok catch se nikdy nespustí. K ověření, že je vyvolána výjimka, použijte metody Assert.Throws, Assert.ThrowsExactly, Assert.ThrowsAsync nebo Assert.ThrowsExactlyAsync a poté proveďte další kontrolní výrazy nad zachycenou výjimkou bez použití bloku try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index 99cde1ab58..3f1f7aa256 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -1015,6 +1015,21 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte Die Verwendung von Asserts in Catch-Blöcken ist problematisch, da der Test auch dann erfolgreich ist, wenn keine Ausnahme ausgelöst wird und der Catch-Block nie ausgeführt wird. Verwenden Sie „Assert.Throws“, „Assert.ThrowsExactly“, „Assert.ThrowsAsync“ oder „Assert.ThrowsExactlyAsync“, um zu überprüfen, ob eine Ausnahme ausgelöst wird, und erstellen Sie dann zusätzliche Assertionen für die abgefangene Ausnahme, ohne den „try-catch“-Block zu verwenden. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 0213f7dc26..3cd4588540 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -1014,6 +1014,21 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: El uso de aserciones en bloques catch es problemático porque la prueba se superará incluso aunque no se produzca ninguna excepción y el bloque catch nunca se ejecuta. Use "Assert.Throws", "Assert.ThrowsExactly", "Assert.ThrowsAsync" o "Assert.ThrowsExactlyAsync" para comprobar que se produzca una excepción y, a continuación, realice aserciones adicionales en la excepción detectada sin usar el bloque try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index a7d3092250..825bb6d99a 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -1014,6 +1014,21 @@ Le type doit être une classe L’utilisation d’assertions dans les blocs catch pose problème, car le test réussit même si aucune exception n’est levée et que le bloc catch n’est jamais exécuté. Utilisez « Assert.Throws », « Assert.ThrowsExactly », « Assert.ThrowsAsync » ou « Assert.ThrowsExactlyAsync » pour vérifier qu’une exception est levée, puis effectuez des assertions supplémentaires sur l’exception capturée sans utiliser le bloc try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index 485a0cb6f0..8e8a930ce3 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -1014,6 +1014,21 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: L'uso di asserzioni nei blocchi catch è problematico perché il test risulta superato anche se non viene generata alcuna eccezione e il blocco catch non viene mai eseguito. Utilizzare 'Assert.Throws', 'Assert.ThrowsExactly', 'Assert.ThrowsAsync' o 'Assert.ThrowsExactlyAsync' per verificare che venga generata un'eccezione, quindi effettuare ulteriori asserzioni sull'eccezione rilevata senza usare il blocco try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index fe8619a141..55ea85ccc9 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -1014,6 +1014,21 @@ The type declaring these methods should also respect the following rules: catch ブロックでアサートを使用すると、例外がスローされず、catch ブロックが実行されなくてもテストが成功するため、問題があります。'Assert.Throws'、'Assert.ThrowsExactly'、'Assert.ThrowsAsync'、または 'Assert.ThrowsExactlyAsync' を使用して例外がスローされたことを確認し、try-catch ブロックを使用せずにキャッチされた例外に対して追加のアサートを実行します。 + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 2588630282..df1eda5fc3 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -1014,6 +1014,21 @@ The type declaring these methods should also respect the following rules: 예외가 발생하지 않고 catch 블록이 실행되지 않더라도 테스트가 통과하기 때문에 catch 블록에서 어설션을 사용하는 것은 문제가 됩니다. 'Assert.Throws', 'Assert.ThrowsExactly', 'Assert.ThrowsAsync' 또는 'Assert.ThrowsExactlyAsync'를 사용하여 예외가 발생했는지 확인한 다음 try-catch 블록을 사용하지 않고 catch된 예외에 대해 추가 어설션을 만듭니다. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 3ed90ee583..73e796730c 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -1014,6 +1014,21 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Używanie asercji w blokach catch jest problematyczne, ponieważ test zakończy się powodzeniem, nawet jeśli nie zostanie zgłoszony żaden wyjątek i blok catch nigdy nie zostanie wykonany. Użyj instrukcji „Assert.Throws”, „Assert.ThrowsExactly”, „Assert.ThrowsAsync” lub „Assert.ThrowsExactlyAsync”, aby sprawdzić, czy zgłoszono wyjątek, a następnie wykonaj dodatkowe asercje dla przechwyconego wyjątku bez użycia bloku try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index 6faf084979..0b59dcdb00 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -1014,6 +1014,21 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Usar assertivas em blocos catch é problemático porque o teste será aprovado mesmo que nenhuma exceção seja lançada e o bloco catch nunca seja executado. Use 'Assert.Throws', 'Assert.ThrowsExactly', 'Assert.ThrowsAsync' ou 'Assert.ThrowsExactlyAsync' para verificar se uma exceção foi lançada e, em seguida, faça asserções adicionais sobre a exceção capturada sem usar o bloco try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index d750be5bc5..0d3df6ab56 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -1026,6 +1026,21 @@ The type declaring these methods should also respect the following rules: Использование утверждений в блоках catch является проблематичным, так как тест будет пройден, даже если не возникает исключение, а блок catch не выполняется. Используйте "Assert.Throws", "Assert.ThrowsExactly", "Assert.ThrowsAsync" или "Assert.ThrowsExactlyAsync" для проверки возникновения исключения, а затем выполните дополнительные утверждения перехваченного исключения без применения блока try-catch. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index 3a0d151f19..957186a9da 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -1016,6 +1016,21 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: Catch bloklarında assert kullanmak sorunludur, çünkü özel durum atılmasa ve catch bloğu hiç çalıştırılmasa bile test geçecektir. Bir özel durumun atıldığını doğrulamak için ‘Assert.Throws’, ‘Assert.ThrowsExactly’, ‘Assert.ThrowsAsync’ veya ‘Assert.ThrowsExactlyAsync’ kullanın ve ardından try-catch bloğu kullanmadan yakalanan özel durum üzerinde ek doğrulamalar yapın. + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index 2b56156c7f..dc529f1d27 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -1014,6 +1014,21 @@ The type declaring these methods should also respect the following rules: 在 catch 块中使用断言是有问题的,因为即使没有引发异常且 catch 块从未执行,测试也会通过。请使用 "Assert.Throws"、"Assert.ThrowsExactly"、"Assert.ThrowsAsync" 或 "Assert.ThrowsExactlyAsync" 来验证是否引发了异常,然后对捕获的异常进行额外断言,而无需使用 try-catch 块。 + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index caf46d3a24..2a011f1a89 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -1014,6 +1014,21 @@ The type declaring these methods should also respect the following rules: 在 Catch 區塊中使用斷言會有問題,因為即使沒有拋出例外且 Catch 區塊從未執行,測試仍會通過。請使用 'Assert.Throws'、'Assert.ThrowsExactly'、'Assert.ThrowsAsync' 或 'Assert.ThrowsExactlyAsync' 來驗證是否有拋出例外,然後在不使用 try-catch 區塊的情況下,對捕捉到的例外進行進一步判斷提示。 + + Avoid duplicate test method attributes + Avoid duplicate test method attributes + + + + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + Test method '{0}' has multiple attributes that inherit from 'TestMethodAttribute'. Only one test method attribute should be used. + + + + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + A test method should be marked with exactly one attribute that is or inherits from 'TestMethodAttribute'. When multiple such attributes are present, only the first one returned by reflection will be used. + + \ No newline at end of file diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateTestMethodAttributeAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateTestMethodAttributeAnalyzerTests.cs new file mode 100644 index 0000000000..60cbb815fe --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/DuplicateTestMethodAttributeAnalyzerTests.cs @@ -0,0 +1,213 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using VerifyCS = MSTest.Analyzers.Test.CSharpCodeFixVerifier< + MSTest.Analyzers.DuplicateTestMethodAttributeAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class DuplicateTestMethodAttributeAnalyzerTests +{ + [TestMethod] + public async Task WhenTestMethodHasSingleAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodHasSingleDerivedAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Runtime.CompilerServices; + + public class MyTestMethod : TestMethodAttribute + { + public MyTestMethod([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + : base(callerFilePath, callerLineNumber) + { + } + } + + [TestClass] + public class MyTestClass + { + [MyTestMethod] + public void TestMethod1() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodHasDataTestMethodAttribute_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DataTestMethod] + [DataRow(1)] + public void TestMethod1(int value) + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodHasBothTestMethodAndDerivedAttribute_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Runtime.CompilerServices; + + public class MyTestMethod : TestMethodAttribute + { + public MyTestMethod([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + : base(callerFilePath, callerLineNumber) + { + } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + [MyTestMethod] + public void [|TestMethod1|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodHasTestMethodAndDataTestMethodAttribute_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + [DataTestMethod] + public void [|TestMethod1|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodHasMultipleDerivedAttributes_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Runtime.CompilerServices; + + public class MyTestMethod1 : TestMethodAttribute + { + public MyTestMethod1([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + : base(callerFilePath, callerLineNumber) + { + } + } + + public class MyTestMethod2 : TestMethodAttribute + { + public MyTestMethod2([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + : base(callerFilePath, callerLineNumber) + { + } + } + + [TestClass] + public class MyTestClass + { + [MyTestMethod1] + [MyTestMethod2] + public void [|TestMethod1|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodHasThreeTestMethodAttributes_Diagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + using System.Runtime.CompilerServices; + + public class MyTestMethod : TestMethodAttribute + { + public MyTestMethod([CallerFilePath] string callerFilePath = "", [CallerLineNumber] int callerLineNumber = -1) + : base(callerFilePath, callerLineNumber) + { + } + } + + [TestClass] + public class MyTestClass + { + [TestMethod] + [DataTestMethod] + [MyTestMethod] + public void [|TestMethod1|]() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenNonTestMethodHasDuplicateAttributes_NoDiagnostic() + { + string code = """ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public void RegularMethod() + { + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +}