在本文中,你将了解 MSTest 用来帮助你编写和塑造测试的 API 和约定。
注意
以“Attribute”结尾的属性名称可以使用短格式。
TestClass 并且 TestClassAttribute 是等效的。 具有无参数构造函数的属性可以省略括号。
测试结构
每个 MSTest 测试类都必须具有 TestClass 该属性,并且每个测试方法都必须具有该 TestMethod 属性:
using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void Add_TwoNumbers_ReturnsSum()
{
// Arrange
var calculator = new Calculator();
// Act
var result = calculator.Add(2, 3);
// Assert
Assert.AreEqual(5, result);
}
}
TestClassAttribute
标记 TestClassAttribute 包含测试和(可选)初始化或清理方法的类。 可以扩展此属性以自定义测试类行为。
TestMethodAttribute
将 TestMethodAttribute 方法标记为要运行的测试。 测试方法必须是:
- 实例方法(非静态)
- 公众
- 返回
void、Task或ValueTask(MSTest v3.3+) - 无参数,除非使用 数据驱动属性
[TestClass]
public class TestMethodExamples
{
[TestMethod]
public void SynchronousTest()
{
Assert.IsTrue(true);
}
[TestMethod]
public async Task AsynchronousTest()
{
await Task.Delay(100);
Assert.IsTrue(true);
}
}
警告
请勿在测试方法中使用async void。 请改用 async Task 或 async ValueTask。
DiscoverInternalsAttribute
程序集 DiscoverInternalsAttribute 属性使 MSTest 能够发现 internal 测试类和方法。 默认情况下,仅发现 public 测试。 如果参数化测试使用内部类型作为参数,则此属性特别有用:
[assembly: DiscoverInternals]
internal record TestInput(int Value, string Description);
[TestClass]
public class CalculatorTests
{
internal static IEnumerable<TestInput> TestData
{
get
{
yield return new TestInput(1, "one");
yield return new TestInput(2, "two");
}
}
[TestMethod]
[DynamicData(nameof(TestData))]
internal void Add_WithTestInput_ReturnsExpected(TestInput input)
{
var calculator = new Calculator();
var result = calculator.Add(input.Value, 1);
Assert.AreEqual(input.Value + 1, result);
}
}
如果没有 DiscoverInternals,测试方法及其内部 TestInput 参数类型将不会由测试运行程序发现。
核心概念
MSTest 文档按主题进行组织:
| 主题 | Description |
|---|---|
| 断言 | 使用 Assert 类验证预期结果 |
| 数据驱动的测试 | 使用多个输入运行测试(DataRow, DynamicData) |
| 测试生命周期 | 程序集、类和测试级别的设置与清理 |
| 执行控件 | 线程处理、并行化、超时、重试和条件执行 |
| 测试组织 | 类别、优先级、所有者和元数据 |
| TestContext | 访问测试运行时信息 |
属性快速参考
| 类别 | 特性 | 请参阅 |
|---|---|---|
| 测试标识 |
TestClass、TestMethod、DiscoverInternals |
此页面 |
| 数据驱动 |
DataRow、DynamicData、TestDataRow |
数据驱动的测试 |
| Lifecycle |
AssemblyInitialize、 ClassInitialize、 TestInitialize和清理对应项 |
测试生命周期 |
| 线程 |
STATestClass、STATestMethod、UITestMethod |
执行控件 |
| 并行化 |
Parallelize、DoNotParallelize |
执行控件 |
| 超时/重试 |
Timeout、Retry |
执行控件 |
| 有條件的 |
Ignore、OSCondition、CICondition |
执行控件 |
| Metadata |
TestCategory、TestProperty、Owner、Priority |
测试组织 |
| 跟踪工作 |
WorkItem、GitHubWorkItem |
测试组织 |
断言
使用 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间的 Assert 类来验证特定功能。 测试方法在应用程序中执行代码,只有当包含 Assert 语句时才报告正确性。
MSTest 断言分为:
-
Assert类:通用断言(AreEqual、IsTrue、ThrowsException) -
StringAssert类:特定于字符串的断言(Contains、、MatchesStartsWith) -
CollectionAssert类:集合断言(Contains、AllItemsAreUnique、AreEquivalent)
测试私有成员
可以使用反射包装类测试专用成员:
- PrivateObject:对于专用实例方法
- PrivateType:对于专用静态方法
小窍门
考虑专用方法是否需要直接测试。 通常,通过公共接口进行测试可提供更好的覆盖范围和更易于维护的测试。