演练:在VS2010中创建并运行单元测试

摘自MSDN,略作整理

 

目录

概述... 1

系统必备... 2

演练... 3

准备演练... 3

创建单元测试... 4

创建单元测试... 4

运行和自定义单元测试... 5

运行和自定义单元测试... 5

运行单元测试并修复代码... 6

运行单元测试并修复代码... 6

为私有方法创建和运行单元测试... 6

为私有方法创建和运行单元测试... 6

运行测试并查看代码覆盖率... 7

运行测试并查看代码覆盖率... 7

 

 

 

 

概述

本演练将逐步指导您使用 Microsoft Visual Studio 2010 来创建、运行和自定义一系列测试。您将从正处于开发过程中的 C# 项目开始,创建执行该项目代码的测试,运行测试并检查结果。然后,可以更改项目代码并重新运行测试。

在本演练中,您将完成以下任务:

·         准备一个要在演练中使用的银行帐户 Woodgrove Bank)项目。

·         打开一个现有项目。

·         为公共和私有方法创建单元测试。

·         对代码运行这些测试。

·         在测试中查找错误并进行更正。

·         在代码中查找错误并进行更正。

系统必备

·         Woodgrove Bank 项目,由简单程序的代码组成。然后可以生成测试 Woodgrove Bank 程序各种方法(公共和私有方法)的单元测试。

using System;

namespace BankAccountNS

{

    /// <summary>

    /// Bank Account demo class./// </summary>

    public class BankAccount

    {   private string m_customerName;

        private double m_balance;

        private bool m_frozen = false;

       

private BankAccount()  { }

       

public BankAccount(string customerName, double balance)

        {    m_customerName = customerName;

            m_balance = balance;

        }

       

public string CustomerName

        {   get { return m_customerName; }

        }

 

        public double Balance

        {   get { return m_balance; }

        }

 

        public void Debit(double amount)

        {   if (m_frozen)

            {   throw new Exception("Account frozen");

            }

            if (amount > m_balance)

            {   throw new ArgumentOutOfRangeException("amount");

            }

            if (amount < 0)

            {   throw new ArgumentOutOfRangeException("amount");

            }

            m_balance += amount;

        }

 

        public void Credit(double amount)

        {   if (m_frozen)

            {   throw new Exception("Account frozen");

            }

            if (amount < 0)

            {   throw new ArgumentOutOfRangeException("amount");

            }

            m_balance += amount;

        }

 

        private void FreezeAccount()

        {   m_frozen = true;

        }

 

        private void UnfreezeAccount()

        {   m_frozen = false;

        }

 

        public static void Main()

        {   BankAccount ba = new BankAccount("Mr.Bryan Walton", 11.99);

            ba.Credit(5.77);

            ba.Debit(11.22);

            Console.WriteLine("Current balance is ${0}", ba.Balance);

        }

    }

}

演练

准备演练

  1. 打开 Visual Studio
  2. 文件菜单上指向 新建,然后单击 项目

此时将出现 新建项目对话框。

  1. 已安装的模板下单击 “Visual C#”
  2. 在应用程序类型的列表中单击 类库
  3. 名称框中键入 Bank,然后单击 确定

说明

如果名称“Bank”已被使用,请为该项目选择其他名称。

  1. 将创建新的 Bank 项目并将其显示在解决方案资源管理器中,而且将在代码编辑器中打开 Class1.cs 文件。

说明

如果代码编辑器中未打开 Class1.cs 文件,请在解决方案资源管理器中双击文件 Class1.cs 将其打开。

  1. 上面“系统必备”中复制源代码。
  2. 用上面“系统必备”中的代码替换 Class1.cs 的原始内容。
  3. 生成菜单上,单击 生成解决方案

现在您有一个名为“Bank”的项目。它包含要测试的源代码和用于对该源代码进行测试的工具。Bank 的命名空间 BankAccountNS包含公共类 BankAccount,在以下过程中将对该类的方法进行测试。

创建单元测试

创建单元测试

  1. 如果代码编辑器中未打开 Class1.cs 文件,请在解决方案资源管理器中双击 Bank 项目中的 Class1.cs 文件。
  2. Class1.cs 文件的 BankAccount类中,滚动到 “Debit()”方法。
  3. 右击 “Debit()”方法,然后选择 创建单元测试

将显示 创建单元测试对话框。

当前选择下,树结构将显示保存 BankAccount类的程序集的类和成员层次结构。 使用此页可以为选定的任何成员生成单元测试,并可以选择要在其中放置生成的单元测试的测试项目。

在该树结构中,只选择了 “Debit()”方法。 将其保留选定状态并选择 “Credit()”方法。

  1. 对于 输出项目,请选择 创建新的 Visual C# 测试项目
  2. 单击 设置

随即出现 测试生成设置对话框。 命名设置下,可以更改测试文件、测试类和测试方法在生成时的命名方式。 常规下,可以更改测试生成的其他方面。 将这些设置保留为默认值,然后单击 确定

  1. 创建单元测试对话框中单击 确定

随即出现 新建测试项目对话框。

  1. 接受默认名称,然后单击 创建

这将创建一个名为 TestProject1 的项目,该项目将显示在解决方案资源管理器中。

一个名为 BankAccountTest.cs 的文件添加到 TestProject1 中,该文件包含一个测试类。该类中填充有一个 TestContext 属性以及测试 “Debit()” “Credit()”方法的方法。

说明

将自动为每个测试方法分配 TestMethod()”特性。 每个测试都与要测试的测试代码中的一个方法相对应。测试方法保存在已分配 TestClass()”特性的测试类中。

  1. BankAccountTest.cs 中,指定要测试的变量的值。滚动到 DebitTest方法,在此可以看到 // TODO 行,它们指示要设置的变量。
  2. 若要了解 DebitTest 方法将使用哪些值,请打开 Class1.cs 文件并滚动到 “Main”方法。 请注意,客户名称初始化为 “Mr. Bryan Walton” 帐户余额初始化为 “11.99”,调用 “Credit”方法时使用参数 “5.77”,调用 “Debit”方法时使用参数 “11.22” 因此,如果此帐户开始时 “Balance” “11.99”,则在传递 “11.22”时调用 “Debit”方法应生成新 “Balance” “0.77”

说明

在本演练后面的部分中,您将使用此预期的 “Balance” (0.77)

  1. BankAccountTest.cs 文件中,滚动到 DebitTest方法。
  2. 设置下列值:

BankAccount target = new BankAccount("Mr.Bryan Walton", 11.99);

double amount = 11.22;

BankAccount target = new BankAccount("Mr.Bryan Walton", 11.99);

double amount = 11.22;

  1.  

CreditTest方法中,将“Mr. Bryan Walton” 11.99 添加到新的 BankAccount 中。

  1. 保存 BankAccountTest.cs 文件。

您已创建了一个源代码文件,其中包含 Bank 项目的测试。现在可以对 Bank 项目的代码运行 BankAccountTest类中的测试了。

运行和自定义单元测试

运行和自定义单元测试

  1. 测试菜单上,单击 窗口,然后选择 测试视图

将显示测试视图窗口。

  1. 右击 DebitTest,再单击 运行选定内容

如果 测试结果窗口尚未打开,则它现在将打开。 DebitTest测试运行。

测试结果窗口的 结果列中,当测试运行时,测试状态将显示为 正在运行 测试运行完成后,测试的结果将更改为 没有结论

  1. 测试结果窗口中右表示测试的行,然后单击 查看测试结果详细信息
  2. 测试结果详细信息页中,将显示错误消息Assert.Inconclusive 失败。 无法验证不返回值的方法。若要创建成功的测试,请先查找并评估此 “Assert”语句。
  3. 若要查找包含 “Assert”语句的测试方法,请打开 BankAccountTest.cs 文件并滚动到 DebitTest()”方法。
  4. Assert 语句是 DebitTest方法中的最后一行。 它显示如下内容:

Assert.Inconclusive("无法验证不返回值的方法。");

Assert.Inconclusive("A method that does not return a value cannot be verified.");

注释掉此 Assert 语句。

  1. 如果现在运行测试,则会得出 已通过结果,但这仅仅是因为它没有对任何内容进行测试。 必须添加对预期的结果进行测试的代码。向 DebitTest方法的末尾添加以下语句:

Assert.AreEqual((System.Convert.ToDouble(0.77)), target.Balance, 0.05);

Assert.AreEqual((System.Convert.ToDouble(0.77)), target.Balance, 0.05);

此语句将预期结果 (0.77) 与调用 BankAccount类的 “Balance”方法所产生的实际结果进行比较。 如果两个值不相等,则 “Assert”返回 “False”,从而使测试失败。

说明

“Assert”语句还包括第三个参数 “delta”,其值为 0.05 Assert.AreEqual方法的此重载中需要该 delta 参数;它可以补偿 “Doubles”等浮点型所固有的舍入错误。

您已运行了 BankAccountTest测试类的生成的 DebitTest方法,注意它需要做的更改,请就此做出这些更改。 现在,可以测试您应用程序中 “Debit”方法的精确性。

运行单元测试并修复代码

运行单元测试并修复代码

  1. 再次运行 Debit 测试:在文件 BankAccountTest.cs 中,右击 DebitTest()”方法,然后单击 运行测试

测试结果窗口的 结果列中,当测试运行时,测试状态将显示为 正在运行 测试运行完成后,测试的结果将更改为 未通过

  1. 测试结果窗口中右表示测试的行,然后单击 查看测试结果详细信息

这将打开测试结果详细信息页,其中显示以下错误消息:Assert.AreEqual 失败。预期值 <0.77> 和实际值 <23.21> 之间的差不应大于 <0.05>”。这些数字似乎表明数学运算不正确。由于 BankAccountTest类的 DebitTest方法测试 BankAccount类的 “Debit”方法,所以从检查 “Debit”方法开始。

  1. 打开 Class1.cs 文件并滚动到 “Debit”方法。
  2. 请注意以下赋值:

m_balance += amount;

m_balance += amount;

此赋值向余额增加金额,在 “Debit”方法中,应当减去赋值。 将此行更改为:

m_balance -= amount;

m_balance -= amount;

  1.  

再次运行 “Debit”测试。

测试结果窗口的 结果列中,将为 DebitTest显示 已通过

说明

更改源代码后不必重新生成测试项目,因为运行测试时会生成项目而不进行提示。

您创建了一个可以运行的单元测试,并通过它查找和修复了代码中的错误。

为私有方法创建和运行单元测试

为私有方法创建和运行单元测试

  1. 打开 Bank 项目中的 Class1.cs 文件。
  2. 右击 FreezeAccount()”方法,然后选择 创建单元测试

随即出现 创建单元测试对话框。

在显示的树结构中,只有 FreezeAccount()”方法处于选定状态。

  1. (可选)单击 筛选器,然后清除 显示非公共项 注意, FreezeAccount()”方法已从 BankAccount 类的子方法列表中移除。 再次单击 筛选器,然后选择 显示非公共项以重新显示 FreezeAccount()”方法。
  2. 确保 FreezeAccount()”方法处于选中状态,然后单击 确定

此时将新建一个名为 Bank.accessor 的专用访问器文件。该文件中包含特殊的访问器方法,测试使用这些方法间接调用 BankAccount 类中的私有方法。在解决方案资源管理器的测试引用文件夹中可以看到这个新文件。

  1. 打开 BankAccountTest.cs 文件并滚动到 FreezeAccountTest()”方法。
  2. 更改 FreezeAccountTest()”方法的代码,使其与下面所示的代码一致。 更改过的区域或新区域有相应的指示:

public void FreezeAccountTest()

{

    BankAccount_Accessor target = new BankAccount_Accessor("Mr.Bryan Walton", 11.99); // TODO: Initialize to an appropriate value

target.FreezeAccount();

    // Assert.Inconclusive("A method that does not return a value cannot be verified.");        bool creditAccount = false; // False means account could be credited: Fail test.     // Try to credit account    try    {        target.Credit(1.00);     }    catch (System.Exception)    {// Threw exception.FreezeAccount worked correctly: Pass test.        creditAccount = true;    }    // Assert fails if 'creditAccount' condition is false.Fail test.    Assert.IsTrue(creditAccount, "Was able to credit account.");

}

public void FreezeAccountTest()

{

    BankAccount_Accessor target = new BankAccount_Accessor("Mr.Bryan Walton", 11.99); // TODO: Initialize to an appropriate value

target.FreezeAccount();

    // Assert.Inconclusive("A method that does not return a value cannot be verified.");        bool creditAccount = false; // False means account could be credited: Fail test.     // Try to credit account    try    {        target.Credit(1.00);     }    catch (System.Exception)    {// Threw exception.FreezeAccount worked correctly: Pass test.        creditAccount = true;    }    // Assert fails if 'creditAccount' condition is false.Fail test.    Assert.IsTrue(creditAccount, "Was able to credit account.");

}

  1.  

运行 FreezeAccountTest测试。

测试结果窗口的 结果列中,最终测试状态显示为 已通过 该结果与预期结果一致,原因是测试在调用 FreezeAccount()”方法冻结帐户之后调用了 “Credit()”方法。

您已经添加了一个私有方法,为其创建了新的单元测试并运行了该测试。可以对 balance 变量使用其他边界值(如 15.00)来多次运行该测试。

运行测试并查看代码覆盖率

对程序集中的代码运行测试时,可以通过收集代码覆盖率数据来查看正在测试的项目代码部分。

运行测试并查看代码覆盖率

  1. 请注意解决方案资源管理器中的解决方案的名称。如果使用上述中的项目,则解决方案的名称为 Bank 此解决方案包含测试代码。
  2. 解决方案资源管理器解决方案项下,双击运行配置文件 Local.testsettings

随即出现 测试设置对话框。

  1. 单击 数据和诊断
  2. 选中代码覆盖率 再点击“配置”(在整页的右中)。
  3. 选择要检测的项目下选中 Bank.dll 旁边的框,其路径指示为 <Solution Directory>\Bank\bin\Debug
  4. 在对话框中单击 另存为,再单击 保存 将出现一个消息框,询问您是否要保存现有文件。在消息框中单击 替换现有文件。
  5. 测试菜单上,指向 选择当前测试设置 子菜单将显示解决方案中的所有测试设置。在刚刚编辑的运行配置 (local.testsettings) 旁边放一个选中标记;使其成为当前测试设置。
  6. 测试列表编辑器中,选中 CreditTest DebitTest旁边的复选框并右击,然后单击 运行选中的测试

运行这两个测试。

  1. 测试工具工具栏上单击 代码覆盖率结果

随即打开 代码覆盖率结果窗口。

  1. 代码覆盖率结果窗口中的 层次结构列将显示一个节点,该节点包含最新测试运行中得到的所有代码覆盖率数据。 测试运行节点的命名格式为 <user name>@<computer name> <date> <time>展开此节点。
  2. 展开程序集 Bank.dll 节点、命名空间 BankAccountNS 节点和 BankAccount 类节点。
  3. BankAccount 类中的行表示其方法。此表中的列显示了各个方法、类和整个命名空间的覆盖率统计数据。
  4. 双击 “Debit”方法所在的行。

将打开 Class1.cs 源代码文件并定位到 Debit 方法所在的位置。在此文件中,可以看到代码突出显示效果。用浅蓝色突出显示的代码行已在测试运行中执行过,用浅褐色突出显示的代码行已部分执行过,而用红褐色突出显示的代码行则还没有执行过。可以通过滚动查看此文件中其他方法的覆盖率。

如果在步骤 7 中选中了 TestProject1.dll 的复选框,则可以打开 Class1Test.cs(即包含单元测试的源代码文件)查看执行过的测试方法。其中应用了相同的突出显示方案:浅蓝色指示已执行的代码;浅褐色指示已部分执行的代码路径,红褐色指示测试运行时未经过的代码路径。