From 8646decfc5a2fb37a662d35827fabb706227e7c4 Mon Sep 17 00:00:00 2001 From: Youssef1313 Date: Tue, 14 Jan 2025 14:47:55 +0100 Subject: [PATCH] Implement analyzer to warn for use of Assert methods in async void --- .../AnalyzerReleases.Unshipped.md | 1 + ...dUsingAssertsInAsyncVoidContextAnalyzer.cs | 89 ++++++++++++ .../MSTest.Analyzers/Helpers/DiagnosticIds.cs | 1 + .../MSTest.Analyzers/PublicAPI.Unshipped.txt | 4 + .../MSTest.Analyzers/Resources.Designer.cs | 27 ++++ src/Analyzers/MSTest.Analyzers/Resources.resx | 9 ++ .../MSTest.Analyzers/xlf/Resources.cs.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.de.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.es.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.fr.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.it.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.ja.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.ko.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.pl.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.pt-BR.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.ru.xlf | 15 ++ .../MSTest.Analyzers/xlf/Resources.tr.xlf | 15 ++ .../xlf/Resources.zh-Hans.xlf | 15 ++ .../xlf/Resources.zh-Hant.xlf | 15 ++ ...gAssertsInAsyncVoidContextAnalyzerTests.cs | 134 ++++++++++++++++++ 20 files changed, 460 insertions(+) create mode 100644 src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs create mode 100644 test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index f8b045e159..b814b3c115 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 --------|----------|----------|------- MSTEST0038 | Usage | Warning | AvoidAssertAreSameWithValueTypesAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0038) MSTEST0039 | Usage | Info | UseNewerAssertThrowsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0039) +MSTEST0040 | Usage | Warning | AvoidUsingAssertsInAsyncVoidContextAnalyzer diff --git a/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs new file mode 100644 index 0000000000..43d70b409b --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AvoidUsingAssertsInAsyncVoidContextAnalyzer.cs @@ -0,0 +1,89 @@ +// 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 Microsoft.CodeAnalysis.Operations; + +using MSTest.Analyzers.Helpers; + +namespace MSTest.Analyzers; + +/// +/// MSTEST0040: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AvoidUsingAssertsInAsyncVoidContextAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.AvoidUsingAssertsInAsyncVoidContextTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AvoidUsingAssertsInAsyncVoidContextMessageFormat), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.AvoidUsingAssertsInAsyncVoidContextDescription), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AvoidUsingAssertsInAsyncVoidContextRuleId, + Title, + MessageFormat, + Description, + Category.Usage, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + + context.RegisterCompilationStartAction(context => + { + Compilation compilation = context.Compilation; + INamedTypeSymbol? assertSymbol = compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssert); + if (assertSymbol is not null) + { + context.RegisterOperationAction(context => AnalyzeOperation(context, assertSymbol), OperationKind.Invocation); + } + }); + } + + private static void AnalyzeOperation(OperationAnalysisContext context, INamedTypeSymbol assertSymbol) + { + var operation = (IInvocationOperation)context.Operation; + if (!IsAsyncVoidContext(operation, context.ContainingSymbol) || + !assertSymbol.Equals(operation.TargetMethod.ContainingType, SymbolEqualityComparer.Default)) + { + return; + } + + context.ReportDiagnostic(operation.CreateDiagnostic(Rule)); + } + + private static bool IsAsyncVoidContext(IInvocationOperation invocationOperation, ISymbol containingSymbol) + { + if (containingSymbol is IMethodSymbol { IsAsync: true, ReturnsVoid: true }) + { + return true; + } + + // For the case of anonymous functions or local functions, the ContainingSymbol is the method that contains the anonymous function. + // So, we need to special case this. + IOperation? operation = invocationOperation; + while (operation is not null) + { + if (operation is IAnonymousFunctionOperation { Symbol.IsAsync: true, Symbol.ReturnsVoid: true } or + ILocalFunctionOperation { Symbol.IsAsync: true, Symbol.ReturnsVoid: true }) + { + return true; + } + + operation = operation.Parent; + } + + return false; + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 60450d9c51..df49efa771 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs @@ -44,4 +44,5 @@ internal static class DiagnosticIds public const string UseProperAssertMethodsRuleId = "MSTEST0037"; public const string AvoidAssertAreSameWithValueTypesRuleId = "MSTEST0038"; public const string UseNewerAssertThrowsRuleId = "MSTEST0039"; + public const string AvoidUsingAssertsInAsyncVoidContextRuleId = "MSTEST0040"; } diff --git a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt index 3f880fefe8..41e2eb4a23 100644 --- a/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt +++ b/src/Analyzers/MSTest.Analyzers/PublicAPI.Unshipped.txt @@ -1,5 +1,9 @@ #nullable enable MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.AvoidAssertAreSameWithValueTypesAnalyzer() -> void +MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer +MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer.AvoidUsingAssertsInAsyncVoidContextAnalyzer() -> void override MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void override MSTest.Analyzers.AvoidAssertAreSameWithValueTypesAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray +override MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer.Initialize(Microsoft.CodeAnalysis.Diagnostics.AnalysisContext! context) -> void +override MSTest.Analyzers.AvoidUsingAssertsInAsyncVoidContextAnalyzer.SupportedDiagnostics.get -> System.Collections.Immutable.ImmutableArray diff --git a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs index 96683c893f..3a3fb28261 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs +++ b/src/Analyzers/MSTest.Analyzers/Resources.Designer.cs @@ -234,6 +234,33 @@ internal static string AvoidExpectedExceptionAttributeTitle { } } + /// + /// Looks up a localized string similar to Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions.. + /// + internal static string AvoidUsingAssertsInAsyncVoidContextDescription { + get { + return ResourceManager.GetString("AvoidUsingAssertsInAsyncVoidContextDescription", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test. + /// + internal static string AvoidUsingAssertsInAsyncVoidContextMessageFormat { + get { + return ResourceManager.GetString("AvoidUsingAssertsInAsyncVoidContextMessageFormat", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Avoid doing assertions inside 'async void' contexts. + /// + internal static string AvoidUsingAssertsInAsyncVoidContextTitle { + get { + return ResourceManager.GetString("AvoidUsingAssertsInAsyncVoidContextTitle", resourceCulture); + } + } + /// /// Looks up a localized string similar to Methods marked with '[ClassCleanup]' should follow the following layout to be valid: ///-it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 470293fe98..0c7145f2dd 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -558,4 +558,13 @@ The type declaring these methods should also respect the following rules: Use 'Assert.AreEqual'/'Assert.AreNotEqual' instead of 'Assert.AreSame'/'Assert.AreNotSame' when comparing value types. Passing a value type to 'Assert.AreSame'/'Assert.AreNotSame' will be boxed (creating a new object). Because 'Assert.AreSame'/'Assert.AreNotSame' does the comparison by reference, 'Assert.AreSame' will fail when boxing happens, and 'Assert.AreNotSame' will always pass. + + Avoid doing assertions inside 'async void' contexts + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf index 7e0d3ff59b..4eacd46b97 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -147,6 +147,21 @@ Typ deklarující tyto metody by měl také respektovat následující pravidla: Vyhněte se [ExpectedException] + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf index 9f9b3db605..6fde0af899 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -147,6 +147,21 @@ Der Typ, der diese Methoden deklariert, sollte auch die folgenden Regeln beachte „[ExpectedException]“ vermeiden + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf index 1f9b00a97f..db2898d74f 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -147,6 +147,21 @@ El tipo que declara estos métodos también debe respetar las reglas siguientes: Evitar '[ExpectedException]' + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf index e89cfa1e60..501ee4dc3c 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -147,6 +147,21 @@ Le type doit être une classe Éviter « [ExpectedException] » + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf index 717fc30523..d4eff3cba0 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -147,6 +147,21 @@ Anche il tipo che dichiara questi metodi deve rispettare le regole seguenti: Evita '[ExpectedException]' + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf index d975c377ec..b2afb36f3e 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -147,6 +147,21 @@ The type declaring these methods should also respect the following rules: '[ExpectedException]' を回避する + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf index 978d132ac7..6d082ab48e 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -147,6 +147,21 @@ The type declaring these methods should also respect the following rules: '[ExpectedException]' 사용 지양 + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf index 842e9f1974..4f9e6a9bb4 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -147,6 +147,21 @@ Typ deklarujący te metody powinien również przestrzegać następujących regu Unikaj elementu „[ExpectedException]” + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf index 9f7c169e0c..67f10872cd 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -147,6 +147,21 @@ O tipo que declara esses métodos também deve respeitar as seguintes regras: Evitar '[ExpectedException]' + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf index 05d8602608..9f6e3ae2ae 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -150,6 +150,21 @@ The type declaring these methods should also respect the following rules: Избегать "[ExpectedException]" + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf index 4c2b557734..7318d4a1dd 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -147,6 +147,21 @@ Bu yöntemleri bildiren tipin ayrıca aşağıdaki kurallara uyması gerekir: '[ExpectedException]' kullanmayın + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf index a6e50d1ef6..4252a6543c 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -147,6 +147,21 @@ The type declaring these methods should also respect the following rules: 避免 "[ExpectedException]" + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf index 125e6a1d2b..1de7b7c55e 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -147,6 +147,21 @@ The type declaring these methods should also respect the following rules: 避免 '[ExpectedException]' + + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + Avoid doing assertions in 'async void' methods or lambdas. Exceptions that are thrown in this context will be unobserved exceptions. + + + + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + Avoid doing assertions inside 'async void' methods or lambdas because they may not fail the test + + + + Avoid doing assertions inside 'async void' contexts + Avoid doing assertions inside 'async void' contexts + + Methods marked with '[ClassCleanup]' should follow the following layout to be valid: -it can't be declared on a generic class without the 'InheritanceBehavior' mode is set diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs new file mode 100644 index 0000000000..378eb183f4 --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidUsingAssertsInAsyncVoidContextAnalyzerTests.cs @@ -0,0 +1,134 @@ +// 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.AvoidUsingAssertsInAsyncVoidContextAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.UnitTests; + +[TestClass] +public sealed class AvoidUsingAssertsInAsyncVoidContextAnalyzerTests +{ + [TestMethod] + public async Task UseAssertMethodInAsyncTaskMethod_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task TestMethod() + { + Assert.Fail(""); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseAssertMethodInAsyncTaskDelegate_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + internal delegate Task MyDelegate(); + + [TestMethod] + public void TestMethod() + { + MyDelegate d = async () => + { + await Task.Delay(1); + Assert.Fail(""); + }; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseAssertMethodInAsyncVoidMethod_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async void TestMethod() + { + await Task.Delay(1); + [|Assert.Fail("")|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseAssertMethodInAsyncVoidDelegate_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + internal delegate void MyDelegate(); + + [TestMethod] + public void TestMethod() + { + MyDelegate d = async () => + { + await Task.Delay(1); + [|Assert.Fail("")|]; + }; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } + + [TestMethod] + public async Task UseAssertMethodInAsyncVoidLocalFunction_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void TestMethod() + { + async void d() + { + await Task.Delay(1); + [|Assert.Fail("")|]; + }; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(code, code); + } +}