diff --git a/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md b/src/Analyzers/MSTest.Analyzers/AnalyzerReleases.Unshipped.md index db7d4e4da2..d038eff73f 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 | Design | Warning | AvoidBlockingCallsInTestsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/core/testing/mstest-analyzers/mstest0059) diff --git a/src/Analyzers/MSTest.Analyzers/AvoidBlockingCallsInTestsAnalyzer.cs b/src/Analyzers/MSTest.Analyzers/AvoidBlockingCallsInTestsAnalyzer.cs new file mode 100644 index 0000000000..26cb85062f --- /dev/null +++ b/src/Analyzers/MSTest.Analyzers/AvoidBlockingCallsInTestsAnalyzer.cs @@ -0,0 +1,142 @@ +// 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; + +/// +/// MSTEST0059: . +/// +[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] +public sealed class AvoidBlockingCallsInTestsAnalyzer : DiagnosticAnalyzer +{ + private static readonly LocalizableResourceString Title = new(nameof(Resources.AvoidBlockingCallsInTestsTitle), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString Description = new(nameof(Resources.AvoidBlockingCallsInTestsDescription), Resources.ResourceManager, typeof(Resources)); + private static readonly LocalizableResourceString MessageFormat = new(nameof(Resources.AvoidBlockingCallsInTestsMessageFormat), Resources.ResourceManager, typeof(Resources)); + + internal static readonly DiagnosticDescriptor AvoidBlockingCallsInTestsRule = DiagnosticDescriptorHelper.Create( + DiagnosticIds.AvoidBlockingCallsInTestsRuleId, + Title, + MessageFormat, + Description, + Category.Design, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + /// + public override ImmutableArray SupportedDiagnostics { get; } + = ImmutableArray.Create(AvoidBlockingCallsInTestsRule); + + /// + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(context => + { + // Get the required symbols + if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingThread, out INamedTypeSymbol? threadSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemThreadingTasksTask, out INamedTypeSymbol? taskSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestMethodAttribute, out INamedTypeSymbol? testMethodAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestInitializeAttribute, out INamedTypeSymbol? testInitializeAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingTestCleanupAttribute, out INamedTypeSymbol? testCleanupAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassInitializeAttribute, out INamedTypeSymbol? classInitializeAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingClassCleanupAttribute, out INamedTypeSymbol? classCleanupAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyInitializeAttribute, out INamedTypeSymbol? assemblyInitializeAttributeSymbol) || + !context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftVisualStudioTestToolsUnitTestingAssemblyCleanupAttribute, out INamedTypeSymbol? assemblyCleanupAttributeSymbol)) + { + return; + } + + context.RegisterOperationAction( + context => AnalyzeInvocation(context, threadSymbol, taskSymbol, testMethodAttributeSymbol, testInitializeAttributeSymbol, testCleanupAttributeSymbol, classInitializeAttributeSymbol, classCleanupAttributeSymbol, assemblyInitializeAttributeSymbol, assemblyCleanupAttributeSymbol), + OperationKind.Invocation); + }); + } + + private static void AnalyzeInvocation( + OperationAnalysisContext context, + INamedTypeSymbol threadSymbol, + INamedTypeSymbol taskSymbol, + INamedTypeSymbol testMethodAttributeSymbol, + INamedTypeSymbol testInitializeAttributeSymbol, + INamedTypeSymbol testCleanupAttributeSymbol, + INamedTypeSymbol classInitializeAttributeSymbol, + INamedTypeSymbol classCleanupAttributeSymbol, + INamedTypeSymbol assemblyInitializeAttributeSymbol, + INamedTypeSymbol assemblyCleanupAttributeSymbol) + { + var invocationOperation = (IInvocationOperation)context.Operation; + IMethodSymbol method = invocationOperation.TargetMethod; + + // Check if we're inside a test-related method + if (context.ContainingSymbol is not IMethodSymbol containingMethod) + { + return; + } + + // Check if the containing method is a test method or test fixture method + if (!IsTestRelatedMethod(containingMethod, testMethodAttributeSymbol, testInitializeAttributeSymbol, testCleanupAttributeSymbol, classInitializeAttributeSymbol, classCleanupAttributeSymbol, assemblyInitializeAttributeSymbol, assemblyCleanupAttributeSymbol)) + { + return; + } + + // Check if the invocation is Thread.Sleep + if (SymbolEqualityComparer.Default.Equals(method.ContainingType, threadSymbol) && method.Name == "Sleep") + { + context.ReportDiagnostic(invocationOperation.Syntax.CreateDiagnostic(AvoidBlockingCallsInTestsRule, "Thread.Sleep")); + return; + } + + // Check if the invocation is Task.Wait + if (SymbolEqualityComparer.Default.Equals(method.ContainingType, taskSymbol) && method.Name == "Wait") + { + context.ReportDiagnostic(invocationOperation.Syntax.CreateDiagnostic(AvoidBlockingCallsInTestsRule, "Task.Wait")); + return; + } + } + + private static bool IsTestRelatedMethod( + IMethodSymbol method, + INamedTypeSymbol testMethodAttributeSymbol, + INamedTypeSymbol testInitializeAttributeSymbol, + INamedTypeSymbol testCleanupAttributeSymbol, + INamedTypeSymbol classInitializeAttributeSymbol, + INamedTypeSymbol classCleanupAttributeSymbol, + INamedTypeSymbol assemblyInitializeAttributeSymbol, + INamedTypeSymbol assemblyCleanupAttributeSymbol) + { + ImmutableArray attributes = method.GetAttributes(); + foreach (AttributeData attribute in attributes) + { + if (attribute.AttributeClass is null) + { + continue; + } + + // Check if the method has any test-related attribute + if (attribute.AttributeClass.Inherits(testMethodAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, testInitializeAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, testCleanupAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, classInitializeAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, classCleanupAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, assemblyInitializeAttributeSymbol) || + SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, assemblyCleanupAttributeSymbol)) + { + return true; + } + } + + return false; + } +} diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs b/src/Analyzers/MSTest.Analyzers/Helpers/DiagnosticIds.cs index 15b809366e..5584239e5d 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 AvoidBlockingCallsInTestsRuleId = "MSTEST0059"; } diff --git a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs index f2ed45a0f9..4aac2612c6 100644 --- a/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs +++ b/src/Analyzers/MSTest.Analyzers/Helpers/WellKnownTypeNames.cs @@ -56,4 +56,5 @@ internal static class WellKnownTypeNames public const string SystemThreadingTasksTask1 = "System.Threading.Tasks.Task`1"; public const string SystemThreadingTasksValueTask = "System.Threading.Tasks.ValueTask"; public const string SystemThreadingTasksValueTask1 = "System.Threading.Tasks.ValueTask`1"; + public const string SystemThreadingThread = "System.Threading.Thread"; } diff --git a/src/Analyzers/MSTest.Analyzers/Resources.resx b/src/Analyzers/MSTest.Analyzers/Resources.resx index 85df4da66e..fc7ed0a27c 100644 --- a/src/Analyzers/MSTest.Analyzers/Resources.resx +++ b/src/Analyzers/MSTest.Analyzers/Resources.resx @@ -693,4 +693,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 using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 a416844e86..fb576a25d6 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.cs.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 e19ae35063..8987cddd31 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.de.xlf @@ -1005,6 +1005,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 34fe594c45..3ec4d4a514 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.es.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 5df8892560..49cb747082 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.fr.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 e536fc735f..e2f1ea0da1 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.it.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 8e3595e788..9dd7725313 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ja.xlf @@ -1004,6 +1004,18 @@ The type declaring these methods should also respect the following rules: catch ブロックでアサートを使用すると、例外がスローされず、catch ブロックが実行されなくてもテストが成功するため、問題があります。'Assert.Throws'、'Assert.ThrowsExactly'、'Assert.ThrowsAsync'、または 'Assert.ThrowsExactlyAsync' を使用して例外がスローされたことを確認し、try-catch ブロックを使用せずにキャッチされた例外に対して追加のアサートを実行します。 + + Avoid using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 dfd7749d6f..d849d84eca 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ko.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 5351c55de2..feaa766c9b 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pl.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 3836015abc..1a31733846 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.pt-BR.xlf @@ -1004,6 +1004,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 a946b4772d..fe642be784 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.ru.xlf @@ -1016,6 +1016,18 @@ The type declaring these methods should also respect the following rules: Использование утверждений в блоках catch является проблематичным, так как тест будет пройден, даже если не возникает исключение, а блок catch не выполняется. Используйте "Assert.Throws", "Assert.ThrowsExactly", "Assert.ThrowsAsync" или "Assert.ThrowsExactlyAsync" для проверки возникновения исключения, а затем выполните дополнительные утверждения перехваченного исключения без применения блока try-catch. + + Avoid using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 1042c81aea..79bdf090a9 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.tr.xlf @@ -1006,6 +1006,18 @@ 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 using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 07a93414ca..5bdf1fc89e 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hans.xlf @@ -1004,6 +1004,18 @@ The type declaring these methods should also respect the following rules: 在 catch 块中使用断言是有问题的,因为即使没有引发异常且 catch 块从未执行,测试也会通过。请使用 "Assert.Throws"、"Assert.ThrowsExactly"、"Assert.ThrowsAsync" 或 "Assert.ThrowsExactlyAsync" 来验证是否引发了异常,然后对捕获的异常进行额外断言,而无需使用 try-catch 块。 + + Avoid using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ 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 82434bb956..cd863e5a68 100644 --- a/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf +++ b/src/Analyzers/MSTest.Analyzers/xlf/Resources.zh-Hant.xlf @@ -1004,6 +1004,18 @@ The type declaring these methods should also respect the following rules: 在 Catch 區塊中使用斷言會有問題,因為即使沒有拋出例外且 Catch 區塊從未執行,測試仍會通過。請使用 'Assert.Throws'、'Assert.ThrowsExactly'、'Assert.ThrowsAsync' 或 'Assert.ThrowsExactlyAsync' 來驗證是否有拋出例外,然後在不使用 try-catch 區塊的情況下,對捕捉到的例外進行進一步判斷提示。 + + Avoid using Thread.Sleep or Task.Wait in test methods + Avoid using Thread.Sleep or Task.Wait in test methods + + + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + Avoid using '{0}' in test methods as it can cause flakiness. Consider using asynchronous alternatives. + + + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + Using 'Thread.Sleep' or 'Task.Wait' in test methods can lead to flaky tests when operations don't complete within the specified time frame. Consider using asynchronous alternatives like 'await Task.Delay' or 'await task' instead. + \ No newline at end of file diff --git a/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidBlockingCallsInTestsAnalyzerTests.cs b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidBlockingCallsInTestsAnalyzerTests.cs new file mode 100644 index 0000000000..8613d0e5de --- /dev/null +++ b/test/UnitTests/MSTest.Analyzers.UnitTests/AvoidBlockingCallsInTestsAnalyzerTests.cs @@ -0,0 +1,334 @@ +// 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.AvoidBlockingCallsInTestsAnalyzer, + Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>; + +namespace MSTest.Analyzers.Test; + +[TestClass] +public sealed class AvoidBlockingCallsInTestsAnalyzerTests +{ + [TestMethod] + public async Task WhenTestMethodUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Thread.Sleep(1000)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodUsesTaskWait_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Task task = Task.CompletedTask; + [|task.Wait()|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodUsesTaskWaitWithTimeout_Diagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + Task task = Task.CompletedTask; + [|task.Wait(1000)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestInitializeUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestInitialize] + public void MyTestInitialize() + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestCleanupUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestCleanup] + public void MyTestCleanup() + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenClassInitializeUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassInitialize] + public static void MyClassInitialize(TestContext context) + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenClassCleanupUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [ClassCleanup] + public static void MyClassCleanup() + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssemblyInitializeUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyInitialize] + public static void MyAssemblyInitialize(TestContext context) + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenAssemblyCleanupUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [AssemblyCleanup] + public static void MyAssemblyCleanup() + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenNonTestMethodUsesThreadSleep_NoDiagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public void NonTestMethod() + { + Thread.Sleep(100); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenNonTestMethodUsesTaskWait_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + public void NonTestMethod() + { + Task task = Task.CompletedTask; + task.Wait(); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodUsesTaskDelay_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task MyTestMethod() + { + await Task.Delay(100); + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenTestMethodAwaitsTask_NoDiagnostic() + { + string code = """ + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public async Task MyTestMethod() + { + Task task = Task.CompletedTask; + await task; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenMultipleBlockingCallsInTestMethod_MultipleDiagnostics() + { + string code = """ + using System.Threading; + using System.Threading.Tasks; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [TestMethod] + public void MyTestMethod() + { + [|Thread.Sleep(100)|]; + Task task = Task.CompletedTask; + [|task.Wait()|]; + [|Thread.Sleep(200)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } + + [TestMethod] + public async Task WhenDataTestMethodUsesThreadSleep_Diagnostic() + { + string code = """ + using System.Threading; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class MyTestClass + { + [DataTestMethod] + [DataRow(1)] + public void MyTestMethod(int value) + { + [|Thread.Sleep(100)|]; + } + } + """; + + await VerifyCS.VerifyAnalyzerAsync(code); + } +}