在单元测试中使用 MSTest 框架

MSTest 框架支持 Visual Studio 中的单元测试。 在编写单元测试的代码时使用 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间中的类和成员。 也可以在优化通过代码生成的单元测试时使用它们。

框架成员

为了帮助提供更清晰的单元测试框架概述,本节将 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间的成员组织为相关功能组。

注意

名称以“Attribute”结尾的属性元素可以在末尾带有或不带有“Attribute”的情况下使用,也可以用于带有或不带有括号的无参数构造函数。 例如,以下代码示例的功能相同:

[TestClass()]

[TestClassAttribute()]

[TestClass]

[TestClassAttribute]

用于标识测试类和方法的属性

每个测试类都必须具有 TestClass 属性,每个测试方法都必须具有 TestMethod 属性。 有关详细信息,请参阅单元测试的剖析

TestClassAttribute

TestClass 属性用于标记类,该类包含测试和可选的初始化或清理方法。

可以扩展此属性以更新或扩展行为。

示例:

[TestClass]
public class MyTestClass
{    
}

TestMethodAttribute

TestMethod 属性在 TestClass 内用于定义要运行的实际测试方法。

该方法应是定义为 public voidpublic Task(可选 async)的实例方法并且是无参数的。

示例

[TestClass]
public class MyTestClass
{
    [TestMethod]
    public void TestMethod()
    {
    }
}
[TestClass]
public class MyTestClass
{
    [TestMethod]
    public async Task TestMethod()
    {
    }
}

用于数据驱动的测试的属性

使用以下元素可设置数据驱动的单元测试。 有关详细信息,请参阅创建数据驱动的单元测试使用配置文件定义数据源

DataRow

DataRowAttribute 允许提供调用测试方法时使用的内联数据。 它可以在测试方法上出现一次或多次。 它应与 TestMethodAttributeDataTestMethodAttribute 结合使用。

参数的数量和类型必须与测试方法签名完全匹配。

有效调用的示例:

[TestClass]
public class TestClass
{
    [TestMethod]
    [DataRow(1, "message", true, 2.0)]
    public void TestMethod1(int i, string s, bool b, float f) {}
    
    [TestMethod]
    [DataRow(new string[] { "line1", "line2" })]
    public void TestMethod2(string[] lines) {}

    [TestMethod]
    [DataRow(null)]
    public void TestMethod3(object o) {}

    [TestMethod]
    [DataRow(new string[] { "line1", "line2" }, new string[] { "line1.", "line2." })]
    public void TestMethod4(string[] input, string[] expectedOutput) {}
}

请注意,还可以使用 params 功能捕获 DataRow 的多个输入。

[TestClass]
public class TestClass
{
    [TestMethod]
    [DataRow(1, 2, 3, 4)]
    public void TestMethod(params int[] values) {}
}

无效组合的示例:

[TestClass]
public class TestClass
{
    [TestMethod]
    [DataRow(1, 2)] // Not valid, we are passing 2 inline data but signature expects 1
    public void TestMethod1(int i) {}

    [TestMethod]
    [DataRow(1)] // Not valid, we are passing 1 inline data but signature expects 2
    public void TestMethod2(int i, int j) {}

    [TestMethod]
    [DataRow(1)] // Not valid, count matches but types do not match
    public void TestMethod3(string s) {}
}

注意

从 MSTest v3 开始,当想要传递恰好 2 个数组时,不再需要将第二个数组包装在对象数组中。 以前:[DataRow(new string[] { "a" }, new object[] { new string[] { "b" } })] In v3 onward: [DataRow(new string[] { "a" }, new string[] { "b" })]

可以通过设置 DisplayName 属性来修改 Visual Studio 中使用的显示名称以及每个 DataRowAttribute 实例的记录器。

[TestClass]
public class TestClass
{
    [TestMethod]
    [DataRow(1, 2, DisplayName= "Functional Case FC100.1")]
    public void TestMethod(int i, int j) {}
}

还可以通过继承 DataRowAttribute 来创建自己的专用数据行属性。

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyCustomDataRowAttribute : DataRowAttribute
{
}

[TestClass]
public class TestClass
{
    [TestMethod]
    [MyCustomDataRow(1)]
    public void TestMethod(int i) {}
}

用于提供初始化和清理的属性

使用以下属性之一进行修饰的方法在指定时间进行调用。 有关详细信息,请参阅单元测试的剖析

程序集

AssemblyInitialize 在加载程序集之后进行调用,AssemblyCleanup 在卸载程序集之前进行调用。

标有这些属性的方法应在 TestClass 中定义为 static voidstatic Task,并且只出现一次。 初始化部分需要一个 TestContext 类型的参数,而清理不需要参数。

[TestClass]
public class MyTestClass
{
    [AssemblyInitialize]
    public static void AssemblyInitialize(TestContext testContext)
    {
    }

    [AssemblyCleanup]
    public static void AssemblyCleanup()
    {
    }
}
[TestClass]
public class MyOtherTestClass
{
    [AssemblyInitialize]
    public static async Task AssemblyInitialize(TestContext testContext)
    {
    }

    [AssemblyCleanup]
    public static async Task AssemblyCleanup()
    {
    }
}

ClassInitialize 在加载类之前(但在静态构造函数之后)进行调用,而 ClassCleanup 在卸载类之后进行调用。

可以控制继承行为:仅适用于使用 InheritanceBehavior.None 的当前类或使用 InheritanceBehavior.BeforeEachDerivedClass 的所有派生类。

还可以配置是应在类的末尾还是在程序集的末尾运行类清理(从 MSTest v4 开始不再支持,因为 EndOfClass 是默认的且唯一的类清理行为)。

标有这些属性的方法应在 TestClass 中定义为 static voidstatic Task,并且只出现一次。 初始化部分需要一个 TestContext 类型的参数,而清理不需要参数。

[TestClass]
public class MyTestClass
{
    [ClassInitialize]
    public static void ClassInitialize(TestContext testContext)
    {
    }

    [ClassCleanup]
    public static void ClassCleanup()
    {
    }
}
[TestClass]
public class MyOtherTestClass
{
    [ClassInitialize]
    public static async Task ClassInitialize(TestContext testContext)
    {
    }

    [ClassCleanup]
    public static async Task ClassCleanup()
    {
    }
}

测试

TestInitialize 在测试开始之前进行调用,而 TestCleanup 在测试完成之后进行调用。

TestInitialize 类似于类构造函数,但通常更适合长初始化或异步初始化。 TestInitialize 始终在构造函数之后进行调用,并针对每个测试(包括数据驱动测试的每个数据行)调用一次。

TestCleanup 类似于 Dispose(或 DisposeAsync)类,但通常更适合长时间或异步清理。 TestCleanup 始终在 DisposeAsync/Dispose 之前进行调用,并针对每个测试(包括数据驱动测试的每个数据行)调用一次。

标有这些属性的方法应定义为 voidTask,在 TestClass 中为无参数,并出现一次或多次。

[TestClass]
public class MyTestClass
{
    [TestInitialize]
    public void TestInitialize()
    {
    }

    [TestCleanup]
    public void TestCleanup()
    {
    }
}
[TestClass]
public class MyOtherTestClass
{
    [TestInitialize]
    public async Task TestInitialize()
    {
    }

    [TestCleanup]
    public async Task TestCleanup()
    {
    }
}

单元测试可以通过不同类型的断言、异常和属性的使用来验证特定应用程序行为。 有关详细信息,请参阅使用 Assert 类

TestContext 类

以下特性和分配给它们的值会出现在特定测试方法的 Visual Studio 属性窗口中。 这些属性不应通过单元测试的代码进行访问。 相反,它们会影响使用或运行单元测试的方法(由你通过 Visual Studio IDE 使用或运行,或由 Visual Studio 测试引擎测试或运行)。 例如,其中一些属性在“测试管理器” 窗口和“测试结果” 窗口中显示为列,这意味着可以使用它们对测试和测试结果进行分组和排序。 这样一个属性是 TestPropertyAttribute,可用于将任意元数据添加到单元测试。 例如,可以通过使用 [TestProperty("TestPass", "Accessibility")] 标记单元测试,来使用该属性存储此测试所涵盖的“测试通过”的名称。 还可以使用它存储其所属的测试类型的指示器:[TestProperty("TestKind", "Localization")]。 使用此特性创建的属性以及分配的属性值都会显示在 Visual Studio“属性” 窗口中的标题“测试特定的” 下。

DeploymentItemAttribute

MSTest V2 框架引入了 DeploymentItemAttribute,用于将指定为部署项的文件或文件夹复制到部署目录(无需添加所复制文件在项目文件夹内的 TestResults 文件夹中的自定义输出路径)。 部署目录是所有部署项以及测试项目 DLL 所在的位置。

它既可以用于测试类(标有 TestClass 属性的类),也可以用于测试方法(标有 TestMethod 属性的方法)。

用户可以使用多个属性实例来指定多个项。

可在此处查看其构造函数

示例

[TestClass] 
[DeploymentItem(@"C:\classLevelDepItem.xml")]   // Copy file using some absolute path
public class UnitTest1
{
    [TestMethod]
    [DeploymentItem(@"..\..\methodLevelDepItem1.xml")]   // Copy file using a relative path from the dll output location
    [DeploymentItem(@"C:\DataFiles\methodLevelDepItem2.xml", "SampleDataFiles")]   // File will be added under a SampleDataFiles in the deployment directory
    public void TestMethod1()
    {
        string textFromFile = File.ReadAllText("classLevelDepItem.xml");
    }
}

测试配置类

用于生成报表的属性

本节中的属性将它们修饰的测试方法与 Team Foundation Server 团队项目的项目层次结构中的实体相关。

与专用访问器一起使用的类

你可以生成一个私有方法的单元测试。 该生成会创建专用访问器类,它会实例化 PrivateObject 类的对象。 PrivateObject 类是包装类,它使用反射作为专用访问器进程的一部分。 PrivateType 类非常相似,但是用于调用私有静态方法,而不是调用私有实例方法。

另请参阅