在单元测试中使用 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 void
或 public Task
(可选 async
)的实例方法并且是无参数的。
示例
[TestClass]
public class MyTestClass
{
[TestMethod]
public void TestMethod()
{
}
}
[TestClass]
public class MyTestClass
{
[TestMethod]
public async Task TestMethod()
{
}
}
用于数据驱动的测试的属性
使用以下元素可设置数据驱动的单元测试。 有关详细信息,请参阅创建数据驱动的单元测试和使用配置文件定义数据源。
DataRow
DataRowAttribute
允许提供调用测试方法时使用的内联数据。 它可以在测试方法上出现一次或多次。 它应与 TestMethodAttribute
或 DataTestMethodAttribute
结合使用。
参数的数量和类型必须与测试方法签名完全匹配。
有效调用的示例:
[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 void
或 static 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 void
或 static 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
之前进行调用,并针对每个测试(包括数据驱动测试的每个数据行)调用一次。
标有这些属性的方法应定义为 void
或 Task
,在 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 类非常相似,但是用于调用私有静态方法,而不是调用私有实例方法。