单元测试的结构

更新:2007 年 11 月

单元测试是一个作为使用 C#、Visual Basic 或 Visual C++ 编写的类中的成员的方法。

说明:

有关如何对 C++ 成品代码使用单元测试以及如何使用用 C++ 编写的单元测试的详细信息,请参见 单元测试和 C++

为了使 Team System 测试工具 能够识别出单元测试,此源代码文件必须位于某个测试项目中,而这个项目是 Visual Studio 解决方案的一部分。在生成此项目或生成整个解决方案时,测试项目将生成到包含可执行单元测试的程序集之中。

所有单元测试方法都使用 [TestMethod()] 属性进行标记(在 Visual Basic 中则为 <TestMethod()>),并且是 [TestClass()] 类的成员。而这个类又是在 Microsoft.VisualStudio.TestTools.UnitTesting 命名空间中定义的。在生成单元测试时,您可以看到,在所生成文件的开始处的 using 或 Imports 语句中包含有此命名空间。

单元测试的属性(Attribute 和 Property)

除了单元测试方法的 [TestMethod()] 属性及其包容类的 [TestClass()] 属性之外,可使用其他属性启用特定的单元测试功能。在这些属性中,最主要的属性有 [TestInitialize()] 和 [TestCleanup()]。使用标记有 [TestInitialize()] 的方法对将要在其中运行单元测试的环境的各个方面进行准备;这样做的目的在于为单元测试的运行建立已知的状态。例如,可以使用 [TestInitialize()] 方法复制、更改或创建测试中将要使用的某些数据文件。

在运行完某个测试后,可通过标记有 [TestCleanup()] 的方法将环境返回到已知状态;这可能意味着需要删除文件夹中的文件,或将某个数据库返回到已知状态。例如,在测试了订单录入应用程序中使用的某个方法后,可将库存数据库重置为初始状态。此外,建议您在 [TestCleanup()] 或 ClassCleanup 方法中使用清除代码,而不要在终结器方法中使用此代码。从终结器方法引发的异常不会被捕捉到,并且会导致无法预料的结果。

TestContext 属性是测试类的一个重要属性。此属性包含的信息包括:当前正在运行的单元测试的名称、部署目录、日志文件的名称;对于数据驱动型测试,还包括所连接到的数据库。TestContext 属性返回一个 TestContext 实例。有关更多信息,请参见使用 TestContext 类

单元测试示例

下面的代码段演示一个用 C# 编写的简单的单元测试。

[TestMethod()]
public void DebitTest()
{
    string customerName = "Mr. Bryan Walton"; 
    double balance = 11.99; 
    BankAccount target = new BankAccount(customerName, balance);
    double amount = 11.22; 
    target.Debit(amount);
    Assert.AreEqual((System.Convert.ToDouble(0.77)), target.Balance, 0.05); // 0.05 is tolerance for floating-point comparison
    //Assert.Inconclusive("A method that does not return a value cannot be verified.");
}

若要查看其他示例,请参见 编写数据驱动的单元测试的代码

单元测试的结果

验证是否已通过单元测试有三种方法:

  • 使用一条或多条 Assert 语句验证特定的结果。有关更多信息,请参见使用 Assert 语句。

  • 验证没有引发任何异常。仍然可以使用一条或多条 Assert 语句完成此工作。

  • 验证是否引发了某个特定的异常。可以使用 ExpectedExceptionAttribute 属性实现此目的。

使用 Assert 语句

如果某个测试过程产生的“通过”或“失败”结果对于您而言比测试可能完成的某些操作更有指示意义或更加重要,则应在测试代码中使用一条或多条 Assert 语句。

测试引擎假定所有单元测试在开始时均为通过状态。测试将保持为此状态,直到 Assert 语句产生一个与通过状态相矛盾的结果,从而将此状态从“通过”更改为“失败”或“没有结论”,或者直到引发一个未在 ExpectedExceptionAttribute 属性中指定的异常。换句话说,没有 Assert 语句的单元测试在每次运行时都会产生一个“通过”结果。这并不表明这种测试没有任何价值,使用这种测试的目的可能仅仅是为了执行代码,确信它在运行时不会引发异常。单元测试中执行的代码将作为代码覆盖率统计信息的一部分进行报告,而无论测试是产生了一个确定的结果还是甚至包含有 Assert 语句。

但是,为了验证是否执行了某个特定操作或是否达到了某个特定状态,必须使用 Assert 语句。Microsoft.VisualStudio.TestTools.UnitTesting 命名空间提供了几条可供使用的 Assert 语句。不同的 Assert 语句为您提供了灵活性;例如,可以使用 Assert.Fail() 语句强迫测试失败。当然,除了这些 Assert 语句之外,您还可能可以在 if 块中使用 Assert 语句,从而构造自己的自定义功能。

无论测试返回的结果如何,测试的通过或失败都取决于它所包含的 Assert 语句。如果一个测试包含多条 Assert 语句,该测试的状态将保留为“通过”,直到遇到一条将此状态更改为“失败”或“没有结论”的 Assert 语句。

有关更多信息,请参见使用 Assert 类

请参见

参考

Microsoft.VisualStudio.TestTools.UnitTesting

其他资源

创建单元测试