Бөлісу құралы:


Тестирование выполнения и управления в MSTest

MSTest предоставляет атрибуты для управления выполнением тестов, включая параллелизацию, модели потоков, время ожидания, повторные попытки и условное выполнение на основе платформы или среды.

Атрибуты многопоточности

Атрибуты потоков определяют, какие методы тестирования модели потоков используются. Эти атрибуты важны при тестировании com-компонентов, элементов пользовательского интерфейса или кода с определенными требованиями к потоку.

STATestClassAttribute

Компонент STATestClassAttribute выполняет все методы тестирования в классе (включая ClassInitialize и ClassCleanup) в однопоточной модели (STA). Используйте этот атрибут при тестировании COM-объектов, требующих STA.

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

Замечание

Этот атрибут поддерживается только в Windows в MSTest версии 3.6 и более поздних версиях.

STATestMethodAttribute

Выполняет STATestMethodAttribute определенный метод тестирования в однопоточной квартире. Используйте этот атрибут для отдельных тестов, которые нуждаются в STA, в то время как для других тестов в классе это не требуется.

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

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

Замечание

Этот атрибут поддерживается только в Windows в MSTest версии 3.6 и более поздних версиях.

Сохранение контекста STA для асинхронных продолжений

Начиная с MSTest 4.1, в STATestMethodAttribute добавлено свойство UseSTASynchronizationContext, которое гарантирует, что асинхронные продолжения выполняются в одном и том же потоке STA. При включении атрибут создает настраиваемый SynchronizationContext, который отправляет продолжения обратно в поток STA, что необходимо для тестирования компонентов пользовательского интерфейса, требующих использования потока STA в ходе их асинхронных операций.

[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);
    }
}

Подсказка

Используйте UseSTASynchronizationContext = true при тестировании компонентов Windows Forms или WPF, которые выполняют асинхронные операции и ожидают, что их продолжение будет выполняться в одном потоке.

UITestMethodAttribute

Атрибут UITestMethod планирует тестовое выполнение в потоке пользовательского интерфейса. Этот атрибут предназначен для тестирования приложений UWP и WinUI, требующих доступа к потоку пользовательского интерфейса.

[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);
    }
}

Замечание

Для этого атрибута требуется соответствующий адаптер MSTest для платформ UWP или WinUI. Дополнительные сведения см. в разделе поддержки платформы .

Атрибуты параллелизации

Атрибуты параллелизации определяют, выполняются ли тесты одновременно, повышая время выполнения теста.

ParallelizeAttribute

По умолчанию MSTest выполняет тесты последовательно. Атрибут уровня сборки позволяет параллельное ParallelizeAttribute выполнение тестов.

using Microsoft.VisualStudio.TestTools.UnitTesting;

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

Область параллелизации

Scope Поведение
ClassLevel Несколько классов тестирования выполняются параллельно, но тесты в классе выполняются последовательно.
MethodLevel Отдельные методы тестирования могут выполняться параллельно независимо от их класса.

Рабочие потоки

Свойство Workers задает максимальное количество потоков для параллельного выполнения:

  • 0 (по умолчанию): используйте количество логических процессоров на компьютере
  • Любое положительное целое число: используйте заданное число потоков.
// Parallelize at class level with 2 worker threads
[assembly: Parallelize(Workers = 2, Scope = ExecutionScope.ClassLevel)]

Подсказка

Кроме того, можно настроить параллелизацию с помощью runsettings или testconfig.json без изменения кода.

Подсказка

Включите параллелизацию на уровне сборки по умолчанию, даже если многие тесты в настоящее время требуют последовательного выполнения. Этот подход рекомендует создавать новые тесты, поддерживающие параллельное выполнение с самого начала. Используйте анализатор MSTEST0001 , чтобы убедиться, что сборка явно объявляет намерение параллелизации с [assembly: Parallelize] или [assembly: DoNotParallelize]. После включения параллелизации просмотрите каждый тестовый класс, чтобы определить, поддерживает ли она безопасную одновременную выполнение. Часто исключение только нескольких классов или методов с DoNotParallelize оказывается достаточным, что позволяет запускать большинство тестов параллельно для значительного ускорения выполнения тестов.

DoNotParallelizeAttribute

Система DoNotParallelizeAttribute предотвращает параллельное выполнение для определенных сборок, классов или методов. Используйте этот атрибут, когда тесты разделяют состояние или ресурсы, к которым нельзя безопасно обращаться одновременно.

[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
    }
}

Замечание

Вам потребуется DoNotParallelize только в том случае, если вы включили параллельное выполнение с атрибутом Parallelize .

Атрибуты времени ожидания

Атрибуты времени ожидания препятствуют выполнению тестов на неопределенный срок и помогают выявлять проблемы с производительностью.

TimeoutAttribute

TimeoutAttribute указывает максимальное время (в миллисекундах), за которое может выполняться метод теста или метод фиктуры. Если выполнение превышает это время, тест завершается ошибкой.

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

Подсказка

Вы можете настроить глобальное время ожидания теста с помощью runsettings (TestTimeout) или testconfig.json (timeout.test) без изменения кода.

Применение времени ожидания к методам фикстур

Вы также можете применить тайм-ауты к методам инициализации и очистки:

[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
    }
}

Подсказка

Каждый метод светильника [Timeout] , принимаюющий атрибут, имеет эквивалентный глобальный параметр конфигурации. Настройте время ожидания глобально через runsettings или testconfig.json, используя такие параметры, как TestInitializeTimeout, ClassInitializeTimeout, AssemblyInitializeTimeout, и аналогичные параметры для очистки.

Замечание

Таймауты не гарантированы в точности. Тест прерывается после указанного времени, но фактическая отмена может занять немного больше времени.

Совместная отмена

По умолчанию MSTest оборачивает каждый временной метод тестирования в отдельную задачу или поток. По достижении времени ожидания платформа перестает наблюдать за тестом, но базовая задача продолжает выполняться в фоновом режиме. Это поведение может вызвать проблемы:

  • Метод теста продолжает получать доступ к ресурсам и изменять состояние даже после истечения времени ожидания.
  • Фоновое выполнение может привести к условиям гонки, влияющим на последующие тесты.
  • Каждый раз метод вызывает дополнительные издержки от оболочки задачи или потока.

Начиная с MSTest 3.6 используйте CooperativeCancellation свойство, чтобы избежать этих проблем. В совместном режиме MSTest не упаковывает тест в дополнительную задачу. Вместо этого, когда достигается время ожидания, фреймворк сигнализирует токен отмены. Ваш тестовый код отвечает за регулярную проверку маркера и его корректное завершение.

[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
        }
    }
}

Преимущества совместной отмены:

  • Снижение затрат на производительность (без дополнительной оболочки задач или потоков на тест).
  • Очистка ресурсов стала более эффективной благодаря тому, что ваш код ясно обрабатывает отмену.
  • Соответствует стандартным моделям отмены в .NET.
  • Детерминированное поведение за счет избегания состояний гонки между тестовым кодом и ненаблюдаемым фоновым выполнением.

Замечание

Для кооперативной отмены требуется, чтобы тестовый код регулярно проверял токен отмены. Если ваш код не проверяет токен, тест не остановится при достижении тайм-аута.

Подсказка

Вы можете включить кооперативную отмену глобально для всех атрибутов времени ожидания с помощью runsettings или testconfig.json вместо индивидуальной настройки каждого атрибута.

Подсказка

Связанные анализаторы:

  • MSTEST0045 — рекомендует использовать кооперативную отмену для атрибутов времени ожидания.

Атрибуты повторных попыток

Атрибуты повторных попыток помогают обрабатывать нестабильные тесты, автоматически повторно выполняя не прошедшие тесты.

RetryAttribute

RetryAttribute, представленный в MSTest 3.8, автоматически выполняет повторные попытки методов тестирования, которые завершаются сбоем или истечением времени ожидания. Настройте максимальное количество попыток повторного выполнения, задержку между этими попытками и стратегию отката.

[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
    }
}

Параметры конфигурации

Недвижимость Description По умолчанию
MaxRetryAttempts Максимальное количество попыток повторов (только для чтения, задаётся через конструктор) Обязательно
MillisecondsDelayBetweenRetries Базовая задержка между повторными попытками (в мс) 0
BackoffType Constant или Exponential задержка Constant

Замечание

В методе теста может присутствовать только один RetryAttribute . Нельзя использовать RetryAttribute для методов, которые не помечены как TestMethod.

Подсказка

Связанные анализаторы:

  • MSTEST0043 — рекомендуется использовать RetryAttribute в методах тестирования.

Реализации пользовательских повторных попыток

Создайте пользовательскую логику повторных попыток путем наследования от RetryBaseAttribute:

public class CustomRetryAttribute : RetryBaseAttribute
{
    private readonly int _maxRetries;

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

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

Атрибуты условного выполнения

Атрибуты условного выполнения определяют, выполняются ли тесты на основе определенных условий, таких как операционная система или среда CI.

ConditionBaseAttribute

Это ConditionBaseAttribute абстрактный базовый класс для условного выполнения. MSTest предоставляет несколько встроенных реализаций.

Замечание

По умолчанию атрибуты условия не наследуются. Применение их к базовому классу не влияет на производные классы. Атрибуты настраиваемого условия могут переопределить это поведение путем переопределения AttributeUsage, но это не рекомендуется, чтобы сохранить согласованность со встроенными атрибутами условия.

Подсказка

Связанные анализаторы:

  • MSTEST0041 — рекомендует использовать атрибуты на основе условий с классами тестирования.

OSConditionAttribute

OSConditionAttribute запускает или пропускает тесты в зависимости от операционной системы. Используйте перечисление OperatingSystems флагов, чтобы указать, какие операционные системы применяются.

[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
    }
}

Поддерживаемые операционные системы

OS Description
Windows Microsoft Windows
Linux Дистрибутивы Linux
OSX macOS
FreeBSD FreeBSD

Объедините операционные системы с побитовой операторОМ OR (|).

Подсказка

Связанные анализаторы:

  • MSTEST0061 — рекомендует использовать атрибут OSCondition вместо проверок среды выполнения.

CIConditionAttribute

Элемент CIConditionAttribute выполняет или пропускает тесты в зависимости от того, исполняются ли они в среде непрерывной интеграции.

[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

IgnoreAttribute безусловно пропускает тестовый класс или метод. При необходимости укажите причину пропуска.

Подсказка

Связанный анализатор: MSTEST0015 — метод тестирования не должен игнорироваться. Включите этот анализатор для обнаружения тестов, которые постоянно игнорируются.

[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
}

Когда вы игнорируете тесты из-за известных проблем, используйте WorkItemAttribute или GitHubWorkItemAttribute для отслеживания:

[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
    }
}

Лучшие практики

  1. Используйте параллелизацию мудро: включите параллелизацию для независимых тестов, но используйте DoNotParallelize для тестов с общим состоянием.

  2. Задайте соответствующие значения времени ожидания: выберите время ожидания, которые позволяют нормально выполнять, но перехватывать зависшие тесты. Рассмотрим медленные среды CI.

  3. Предпочитайте совместную отмену: используйте совместную отмену, чтобы избежать дополнительных оболочек задач и предотвратить фоновое выполнение тестов с истекшим временем. Включите анализатор MSTEST0045 для применения этой практики.

  4. Документирование игнорируемых тестов: Всегда указывайте причину и ссылку на рабочий элемент при игнорировании тестов.

  5. Используйте повторные попытки экономно: устраните коренную причину ненадёжных тестов вместо того, чтобы полагаться на повторные попытки.

  6. Тестируйте код для конкретной ОС соответствующим образом: используйте OSCondition для выполнения тестов, зависящих от платформы, только в тех случаях, когда они применимы.

См. также