Udostępnij przez


Uruchamianie i kontrola testów w MSTest

Narzędzie MSTest udostępnia atrybuty do kontrolowania sposobu wykonywania testów, w tym przetwarzania równoległego, modeli wątków, limitów czasu, ponawiania prób i wykonywania warunkowego na podstawie platformy lub środowiska.

Atrybuty wątkowania

Atrybuty wątków kontrolują model wątku, którego używają metody testowe. Te atrybuty są niezbędne podczas testowania składników COM, elementów interfejsu użytkownika lub kodu z określonymi wymaganiami wątkowymi.

STATestClassAttribute

Urządzenie STATestClassAttribute uruchamia wszystkie metody testowe w klasie (w tym ClassInitialize i ClassCleanup) w mieszkaniu jednowątkowym (STA). Użyj tego atrybutu podczas testowania obiektów COM, które wymagają sta.

[STATestClass]
public class ComInteropTests
{
    [TestMethod]
    public void TestComComponent()
    {
        // This test runs in an STA thread
        var comObject = new SomeComObject();
        // Test COM interactions
    }
}

Uwaga / Notatka

Ten atrybut jest obsługiwany tylko w systemie Windows w programie MSTest w wersji 3.6 lub nowszej.

STATestMethodAttribute

Uruchamia STATestMethodAttribute określoną metodę testową w jednym wątkowym mieszkaniu. Użyj tego atrybutu dla poszczególnych testów, które potrzebują STA, podczas gdy inne testy w tej klasie tego nie potrzebują.

[TestClass]
public class MixedThreadingTests
{
    [STATestMethod]
    public void TestRequiringSTA()
    {
        // This test runs in an STA thread
    }

    [TestMethod]
    public void RegularTest()
    {
        // This test uses default threading
    }
}

Uwaga / Notatka

Ten atrybut jest obsługiwany tylko w systemie Windows w programie MSTest w wersji 3.6 lub nowszej.

Zachowywanie kontekstu STA dla kontynuacji asynchronicznych

Począwszy od MSTest 4.1, element STATestMethodAttribute zawiera właściwość UseSTASynchronizationContext, która zapewnia, że kontynuacje asynchroniczne są uruchamiane w tym samym wątku STA. Po włączeniu atrybut tworzy niestandardowy SynchronizationContext element, który publikuje kontynuacje wątku STA, co jest niezbędne do testowania składników interfejsu użytkownika, które wymagają wątkowania STA w ramach operacji asynchronicznych.

[TestClass]
public class UIComponentTests
{
    [STATestMethod(UseSTASynchronizationContext = true)]
    public async Task TestAsyncUIOperation()
    {
        // Initial code runs on STA thread
        var control = new MyControl();
        
        await control.LoadDataAsync();
        
        // Continuation also runs on STA thread,
        // ensuring UI operations remain valid
        Assert.IsTrue(control.IsDataLoaded);
    }
}

Wskazówka

Użyj UseSTASynchronizationContext = true podczas testowania składników Windows Forms lub WPF, które wykonują operacje asynchroniczne i oczekują kontynuacji działania w tym samym wątku.

UITestMethodAttribute

Atrybut UITestMethod planuje wykonywanie testów w wątku interfejsu użytkownika. Ten atrybut jest przeznaczony do testowania aplikacji UWP i WinUI, które wymagają dostępu do wątku interfejsu użytkownika.

[TestClass]
public class WinUITests
{
    [UITestMethod]
    public void TestUIComponent()
    {
        // This test runs on the UI thread
        var button = new Button();
        button.Content = "Click me";
        Assert.IsNotNull(button.Content);
    }
}

Uwaga / Notatka

Ten atrybut wymaga odpowiedniego adaptera MSTest dla platform UWP lub WinUI. Aby uzyskać więcej informacji, zobacz sekcję pomocy technicznej platformy .

Atrybuty równoległościowe

Atrybuty przetwarzania równoległego określają, czy i jak testy są uruchamiane współbieżnie, poprawiając czas wykonywania testu.

ParallelizeAttribute

Domyślnie narzędzie MSTest uruchamia testy sekwencyjnie. Atrybut ParallelizeAttribute na poziomie zestawu umożliwia równoległe wykonywanie testów.

using Microsoft.VisualStudio.TestTools.UnitTesting;

[assembly: Parallelize(Workers = 0, Scope = ExecutionScope.MethodLevel)]

Zakres przetwarzania równoległego

Scope Zachowanie
ClassLevel Wiele klas testowych jest uruchamianych równolegle, ale testy w obrębie klasy są uruchamiane sekwencyjnie.
MethodLevel Poszczególne metody testowe mogą być uruchamiane równolegle, niezależnie od ich klasy

Wątki robocze

Właściwość Workers określa maksymalną liczbę wątków do wykonywania równoległego:

  • 0 (ustawienie domyślne): użyj liczby procesorów logicznych na maszynie
  • Dowolna dodatnia liczba całkowita: użyj tej konkretnej liczby wątków
// Parallelize at class level with 2 worker threads
[assembly: Parallelize(Workers = 2, Scope = ExecutionScope.ClassLevel)]

Wskazówka

Można również skonfigurować równoległość za pomocą runsettings lub testconfig.json bez modyfikowania kodu.

Wskazówka

Domyślnie włącz równoległość na poziomie kompilacji, nawet jeśli wiele testów wymaga wykonywania sekwencyjnego. Takie podejście zachęca do pisania nowych testów, które obsługują wykonywanie równoległe od samego początku. Użyj analizatora MSTEST0001 , aby upewnić się, że każda klasa testowa jawnie deklaruje intencję równoległości, co wymusza sprawdzenie, czy każda klasa bezpiecznie obsługuje wykonywanie współbieżne. Często wykluczenie tylko kilku klas lub metod z DoNotParallelize jest wystarczające, dzięki czemu większość testów może być uruchamiana równolegle w celu znacznie szybszego wykonywania testów.

DoNotParallelizeAttribute

Funkcja DoNotParallelizeAttribute zapobiega równoległym wykonywaniu określonych zestawów, klas lub metod. Użyj tego atrybutu, gdy testy współdzielą stan lub zasoby, do których nie można bezpiecznie uzyskiwać dostępu współbieżnego.

[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]

[TestClass]
public class ParallelTests
{
    [TestMethod]
    public void CanRunInParallel()
    {
        // This test can run with others
    }
}

[TestClass]
[DoNotParallelize]
public class SequentialTests
{
    [TestMethod]
    public void MustRunSequentially()
    {
        // This class's tests run sequentially
    }
}

[TestClass]
public class MixedTests
{
    [TestMethod]
    public void CanRunInParallel()
    {
        // This test can run with others
    }

    [TestMethod]
    [DoNotParallelize]
    public void MustBeIsolated()
    {
        // This specific test doesn't run in parallel
    }
}

Uwaga / Notatka

Wystarczy DoNotParallelize tylko wtedy, gdy włączono wykonywanie równoległe z atrybutem Parallelize .

Atrybuty limitu czasu

Atrybuty limitu czasu uniemożliwiają uruchamianie testów na czas nieokreślony i pomagają identyfikować problemy z wydajnością.

TimeoutAttribute

Parametr TimeoutAttribute określa maksymalny czas (w milisekundach), który może uruchomić metoda testu lub urządzenia. Jeśli wykonanie przekroczy ten czas, test zakończy się niepowodzeniem.

[TestClass]
public class TimeoutTests
{
    [TestMethod]
    [Timeout(5000)] // 5 seconds
    public void TestWithTimeout()
    {
        // Test must complete within 5 seconds
    }
}

Wskazówka

Możesz skonfigurować globalny limit czasu testu za pomocą ustawień runsettings (TestTimeout) lub testconfig.json (timeout.test) bez modyfikowania kodu.

Zastosowanie limitu czasu do metod pomocniczych

Limity czasu można również zastosować do metod inicjalizacji i czyszczenia.

[TestClass]
public class FixtureTimeoutTests
{
    [ClassInitialize]
    [Timeout(10000)]
    public static void ClassInit(TestContext context)
    {
        // Must complete within 10 seconds
    }

    [TestInitialize]
    [Timeout(2000)]
    public void TestInit()
    {
        // Must complete within 2 seconds
    }
}

Wskazówka

Każda metoda fixture'a, która akceptuje atrybut [Timeout], ma równoważne globalne ustawienie konfiguracji. Skonfiguruj globalnie czas oczekiwania za pomocą runsettings lub testconfig.json przy użyciu ustawień, takich jak TestInitializeTimeout, ClassInitializeTimeout, AssemblyInitializeTimeout, i ich odpowiedniki oczyszczania.

Uwaga / Notatka

Czasowe limity nie gwarantują precyzyjności. Test przerywa się po upływie określonego czasu, ale rzeczywiste anulowanie może potrwać nieco dłużej.

Anulowanie współpracy

Domyślnie narzędzie MSTest umieszcza każdą metodę testową w osobnym zadaniu lub wątku. Po osiągnięciu limitu czasu struktura przestaje obserwować test, ale bazowe zadanie będzie nadal działać w tle. To zachowanie może powodować problemy:

  • Metoda testowa nadal uzyskuje dostęp do zasobów i zmienia stan nawet po przekroczeniu limitu czasu.
  • Wykonywanie w tle może prowadzić do warunków wyścigu wpływających na kolejne testy.
  • Każda metoda czasowa wiąże się z dodatkowym narzutem związanym z opakowaniem zadań lub wątków.

Począwszy od msTest 3.6, użyj CooperativeCancellation właściwości , aby uniknąć tych problemów. W trybie kooperacyjnym narzędzie MSTest nie zawija testu w dodatkowe zadanie. Zamiast tego po osiągnięciu limitu czasu platforma sygnalizuje token anulowania. Kod testowy jest odpowiedzialny za regularne sprawdzanie tokenu i łagodne zakończenie działania.

[TestClass]
public class CooperativeTimeoutTests
{
    [TestMethod]
    [Timeout(5000, CooperativeCancellation = true)]
    public async Task TestWithCooperativeCancellation(CancellationToken cancellationToken)
    {
        // Check the token periodically
        while (!cancellationToken.IsCancellationRequested)
        {
            await Task.Delay(100, cancellationToken);
            // Do work
        }
    }

    [TestMethod]
    [Timeout(5000, CooperativeCancellation = true)]
    public void SyncTestWithCooperativeCancellation(CancellationToken cancellationToken)
    {
        // Works with sync methods too
        for (int i = 0; i < 1000; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();
            // Do work
        }
    }
}

Korzyści wynikające z anulowania współpracy:

  • Mniejsze obciążenie wydajności (bez dodatkowej nakładki zadań/wątków na test).
  • Skuteczniejsze czyszczenie zasobów, ponieważ kod jawnie obsługuje anulowanie.
  • Jest zgodny ze standardowymi wzorcami anulowania platformy .NET.
  • Zachowanie deterministyczne dzięki unikaniu warunków wyścigu między kodem testowym a nieobserwowanym wykonywaniem w tle.

Uwaga / Notatka

Współpracujące anulowanie wymaga, aby kod testowy regularnie sprawdzał token anulowania. Jeśli kod nie sprawdzi tokenu, test nie zostanie zatrzymany po osiągnięciu limitu czasu.

Wskazówka

Możesz włączyć globalne anulowanie współpracy dla wszystkich atrybutów limitu czasu za pomocą ustawień uruchamiania lub testconfig.json zamiast ustawiać je osobno dla każdego atrybutu.

Wskazówka

Powiązane analizatory:

  • MSTEST0045 — zaleca użycie anulowania kooperacyjnego dla atrybutów określających limit czasu.

Atrybuty ponawiania próby

Atrybuty ponawiania prób pomagają w obsłudze flakujących testów przez automatyczne ponowne uruchamianie testów, które zakończyły się niepowodzeniem.

RetryAttribute

Metoda RetryAttribute, wprowadzona w MSTest 3.8, automatycznie ponawia próby metod testowych, które kończą się niepowodzeniem lub przekroczeniem limitu czasu. Skonfiguruj maksymalną liczbę ponownych prób, opóźnienie między ponownymi próbami i strategię wycofywania.

[TestClass]
public class RetryTests
{
    [TestMethod]
    [Retry(3)] // Retry up to 3 times if the test fails
    public void FlakeyNetworkTest()
    {
        // Test that might occasionally fail due to network issues
    }

    [TestMethod]
    [Retry(3, MillisecondsDelayBetweenRetries = 1000, BackoffType = DelayBackoffType.Exponential)]
    public void TestWithExponentialBackoff()
    {
        // Retries with increasing delays: 1s, 2s, 4s
    }

    [TestMethod]
    [Retry(5, MillisecondsDelayBetweenRetries = 500, BackoffType = DelayBackoffType.Constant)]
    public void TestWithConstantDelay()
    {
        // Retries with constant 500ms delay between attempts
    }
}

Opcje konfiguracji

Majątek Description Default
MaxRetryAttempts Maksymalna liczba ponownych prób (tylko do odczytu, ustawiona za pomocą konstruktora) Required
MillisecondsDelayBetweenRetries Opóźnienie bazowe między ponownymi próbami (w ms) 0
BackoffType Constant lub Exponential opóźnienie Constant

Uwaga / Notatka

Tylko jeden RetryAttribute może być obecny w metodzie testowej. Nie można stosować RetryAttribute do metod, które nie są oznaczone słowem kluczowym TestMethod.

Wskazówka

Powiązane analizatory:

  • MSTEST0043 — zaleca użycie RetryAttribute metod testowych.

Niestandardowe implementacje ponawiania prób

Utwórz niestandardową logikę ponawiania, dziedzicząc z RetryBaseAttribute:

public class CustomRetryAttribute : RetryBaseAttribute
{
    private readonly int _maxRetries;

    public CustomRetryAttribute(int maxRetries)
    {
        _maxRetries = maxRetries;
    }

    // Implement abstract members
    // Add custom logic for retry conditions
}

Atrybuty wykonywania warunkowego

Atrybuty warunkowego wykonywania określają, czy testy mają być uruchomione na podstawie określonych warunków, takich jak system operacyjny czy środowisko CI.

ConditionBaseAttribute

Jest ConditionBaseAttribute to abstrakcyjna klasa bazowa do wykonywania warunkowego. Narzędzie MSTest udostępnia kilka wbudowanych implementacji.

Uwaga / Notatka

Domyślnie atrybuty warunku nie są dziedziczone. Zastosowanie ich do klasy bazowej nie ma wpływu na klasy pochodne. Atrybuty warunku niestandardowego mogą zastąpić to zachowanie przez ponowne zdefiniowanie AttributeUsage, ale nie jest to zalecane, aby zachować spójność z wbudowanymi atrybutami warunku.

Wskazówka

Powiązane analizatory:

  • MSTEST0041 — zaleca używanie atrybutów opartych na warunku z klasami testowymi.

OSConditionAttribute

Uruchamia lub pomija testy OSConditionAttribute na podstawie systemu operacyjnego. Użyj wyliczenia OperatingSystems flag, aby określić, które systemy operacyjne mają zastosowanie.

[TestClass]
public class OSSpecificTests
{
    [TestMethod]
    [OSCondition(OperatingSystems.Windows)]
    public void WindowsOnlyTest()
    {
        // Runs only on Windows
    }

    [TestMethod]
    [OSCondition(OperatingSystems.Linux | OperatingSystems.OSX)]
    public void UnixLikeOnlyTest()
    {
        // Runs on Linux or macOS
    }

    [TestMethod]
    [OSCondition(ConditionMode.Exclude, OperatingSystems.Windows)]
    public void SkipOnWindowsTest()
    {
        // Runs on any OS except Windows
    }
}

Obsługiwane systemy operacyjne

System operacyjny Description
Windows Microsoft Windows
Linux Dystrybucje systemu Linux
OSX macOS
FreeBSD FreeBSD

Łączenie systemów operacyjnych z operatorem bitowym OR (|).

Wskazówka

Powiązane analizatory:

  • MSTEST0061 — zaleca użycie OSCondition atrybutu zamiast kontroli środowiska uruchomieniowego.

CIConditionAttribute

Uruchamia CIConditionAttribute lub pomija testy na podstawie tego, czy są wykonywane w ramach środowiska ciągłej integracji.

[TestClass]
public class CIAwareTests
{
    [TestMethod]
    [CICondition] // Default: runs only in CI
    public void CIOnlyTest()
    {
        // Runs only in CI environments
    }

    [TestMethod]
    [CICondition(ConditionMode.Include)]
    public void ExplicitCIOnlyTest()
    {
        // Same as above, explicitly stated
    }

    [TestMethod]
    [CICondition(ConditionMode.Exclude)]
    public void LocalDevelopmentOnlyTest()
    {
        // Skipped in CI, runs during local development
    }
}

IgnoreAttribute

Bezwarunkowo IgnoreAttribute pomija klasę testową lub metodę. Opcjonalnie podaj przyczynę ignorowania.

Wskazówka

Powiązany analizator: MSTEST0015 — metoda testowa nie powinna być ignorowana. Włącz ten analizator, aby wykrywać testy, które są trwale ignorowane.

[TestClass]
public class IgnoreExamples
{
    [TestMethod]
    [Ignore]
    public void TemporarilyDisabled()
    {
        // This test is skipped
    }

    [TestMethod]
    [Ignore("Waiting for bug #123 to be fixed")]
    public void DisabledWithReason()
    {
        // This test is skipped with a documented reason
    }
}

[TestClass]
[Ignore("Entire class needs refactoring")]
public class IgnoredTestClass
{
    [TestMethod]
    public void Test1() { }  // Skipped

    [TestMethod]
    public void Test2() { }  // Skipped
}

Podczas ignorowania testów ze względu na znane problemy użyj polecenia WorkItemAttribute lub GitHubWorkItemAttribute w celu śledzenia:

[TestClass]
public class TrackedIgnoreExamples
{
    [TestMethod]
    [Ignore("Waiting for fix")]
    [WorkItem(12345)]
    public void TestWithWorkItem()
    {
        // Linked to work item 12345
    }

    [TestMethod]
    [Ignore("Known issue")]
    [GitHubWorkItem("https://github.com/owner/repo/issues/42")]
    public void TestWithGitHubIssue()
    {
        // Linked to GitHub issue #42
    }
}

Najlepsze rozwiązania

  1. Używaj równoległości mądrze: włącz równoległość dla niezależnych testów, ale użyj DoNotParallelize do testów, które współdzielą stan.

  2. Ustaw odpowiednie limity czasu: wybierz limity czasu, które umożliwiają normalne wykonanie, ale wykrywają zablokowane testy. Rozważ powolne środowiska ciągłej integracji.

  3. Preferuj anulowanie kooperacyjne: używaj anulowania kooperacyjnego, aby uniknąć narzutu nadmiarowych opakowań zadań i zapobiec wykonywaniu testów przekraczających czas w tle. Włącz analizator MSTEST0045 , aby wymusić tę praktykę.

  4. Dokumentowanie ignorowanych testów: zawsze podaj przyczynę i odniesienie do elementu roboczego podczas ignorowania testów.

  5. Użyj ponownych prób oszczędnie: rozwiąż główną przyczynę niestabilnych testów, a nie polegaj na ponownych próbach.

  6. Odpowiednio przetestuj kod specyficzny dla systemu operacyjnego: użyj polecenia OSCondition , aby uruchamiać testy specyficzne dla platformy tylko wtedy, gdy mają zastosowanie.

Zobacz także