MSTest 提供属性来控制测试的执行方式,包括并行化、线程模型、超时、重试和基于平台或环境的条件执行。
线程属性
线程属性控制哪些线程模型测试方法使用。 测试具有特定线程要求的 COM 组件、UI 元素或代码时,这些属性至关重要。
STATestClassAttribute
在c0 的单线程单元(STA)中运行类中的所有测试方法(包括 c1 和 c2)。 测试需要 STA 的 COM 对象时,请使用此属性。
[STATestClass]
public class ComInteropTests
{
[TestMethod]
public void TestComComponent()
{
// This test runs in an STA thread
var comObject = new SomeComObject();
// Test COM interactions
}
}
注释
此属性仅在 MSTest v3.6 及更高版本中的 Windows 上受支持。
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
}
}
注释
此属性仅在 MSTest v3.6 及更高版本中的 Windows 上受支持。
保留异步延续的 STA 上下文
从 MSTest 4.1 开始,STATestMethodAttribute 包含一个 UseSTASynchronizationContext 属性,可确保异步延续在同一 STA 线程上运行。 启用后,该属性将创建一个自定义组件 SynchronizationContext,用于将延续发布回 STA 线程,这对于测试需要在异步操作中执行 STA 线程的 UI 组件至关重要。
[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);
}
}
小窍门
在测试执行异步操作并期望其回调在同一线程上运行的 Windows 窗体或 WPF 组件时,使用UseSTASynchronizationContext = true。
UITestMethodAttribute
该 UITestMethod 属性在 UI 线程上安排测试执行。 此属性旨在测试需要 UI 线程访问的 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);
}
}
注释
此属性需要适用于 UWP 或 WinUI 平台的相应 MSTest 适配器。 有关详细信息,请参阅 平台支持 部分。
并行化属性
并行化属性控制测试是否以及如何并发运行,从而缩短测试执行时间。
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 分析器来确保每个测试类显式声明其并行化意向,从而强制检查每个类是否安全支持并发执行。 通常,只排除几个具有足够条件的 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] 属性的每个装置方法都具有等效的全局配置设置。 通过 ClassInitializeTimeout 或 AssemblyInitializeTimeout 全局配置超时,使用例如、、TestInitializeTimeout及它们的清理对应项的设置。
注释
无法保证超时准确。 测试在指定的时间过后中止,但实际取消可能需要稍长一些时间。
协作取消
默认情况下,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 为所有超时属性全局启用协作取消,而不是单独在每个属性上设置它。
重试属性
重试属性通过自动重新运行失败的测试来帮助处理异常测试。
RetryAttribute
MSTest 3.8 中引入的 RetryAttribute 会自动重试失败或超时的测试方法。配置最大重试尝试次数、重试之间的延迟以及退避策略。
[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 |
重试之间的基本延迟(以 ms 为单位) | 0 |
BackoffType |
Constant 或 Exponential 延迟 |
Constant |
注释
测试方法上只能存在一个 RetryAttribute 。 不能对未标记RetryAttribute的方法使用TestMethod。
自定义重试实现
通过继承自 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来替代此行为,但不建议与内置条件属性保持一致性。
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
}
}
受支持的操作系统
| 操作系统 | Description |
|---|---|
Windows |
Microsoft Windows |
Linux |
Linux 分布 |
OSX |
macOS |
FreeBSD |
FreeBSD |
将操作系统与按位或运算符(|)组合在一起。
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
}
}
最佳做法
明智地使用并行化:为独立测试启用并行化,但对于共享状态的测试,请不要使用并行化
DoNotParallelize。设置适当的超时:选择允许测试正常执行但能捕获卡住情况的超时。 请考虑使用慢速 CI 环境。
首选协作式取消:通过使用协作式取消来避免不必要的任务封装器开销,并防止超时测试在后台执行。 启用 MSTEST0045 分析器以强制实施这种做法。
记录忽略的测试:在忽略测试时始终提供原因和工作项引用。
请谨慎使用重试:解决异常测试的根本原因,而不是依赖重试。
适当地测试特定于操作系统的代码:使用
OSCondition仅在合适时运行平台特定测试。