演练:在VS2010中创建并运行单元测试
摘自MSDN,略作整理
目录
本演练将逐步指导您使用 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);
}
}
}
此时将出现 “新建项目”对话框。
|
如果名称“Bank”已被使用,请为该项目选择其他名称。 |
|
如果代码编辑器中未打开 Class1.cs 文件,请在解决方案资源管理器中双击文件 Class1.cs 将其打开。 |
现在您有一个名为“Bank”的项目。它包含要测试的源代码和用于对该源代码进行测试的工具。Bank 的命名空间 “BankAccountNS”包含公共类 “BankAccount”,在以下过程中将对该类的方法进行测试。
将显示 “创建单元测试”对话框。
在 “当前选择”下,树结构将显示保存 “BankAccount”类的程序集的类和成员层次结构。 使用此页可以为选定的任何成员生成单元测试,并可以选择要在其中放置生成的单元测试的测试项目。
在该树结构中,只选择了 “Debit()”方法。 将其保留选定状态并选择 “Credit()”方法。
随即出现 “测试生成设置”对话框。 在 “命名设置”下,可以更改测试文件、测试类和测试方法在生成时的命名方式。 在 “常规”下,可以更改测试生成的其他方面。 将这些设置保留为默认值,然后单击 “确定”。
随即出现 “新建测试项目”对话框。
这将创建一个名为 TestProject1 的项目,该项目将显示在解决方案资源管理器中。
一个名为 BankAccountTest.cs 的文件添加到 TestProject1 中,该文件包含一个测试类。该类中填充有一个 TestContext 属性以及测试 “Debit()”和 “Credit()”方法的方法。
|
将自动为每个测试方法分配 “TestMethod()”特性。 每个测试都与要测试的测试代码中的一个方法相对应。测试方法保存在已分配 “TestClass()”特性的测试类中。 |
|
在本演练后面的部分中,您将使用此预期的 “Balance”值 (0.77)。 |
BankAccount target = new BankAccount("Mr.Bryan Walton", 11.99);
double amount = 11.22;
在 “CreditTest”方法中,将“Mr. Bryan Walton”和 11.99 添加到新的 BankAccount 中。
您已创建了一个源代码文件,其中包含 Bank 项目的测试。现在可以对 Bank 项目的代码运行 “BankAccountTest”类中的测试了。
将显示“测试视图”窗口。
如果 “测试结果”窗口尚未打开,则它现在将打开。 “DebitTest”测试运行。
在 “测试结果”窗口的 “结果”列中,当测试运行时,测试状态将显示为 “正在运行”。 测试运行完成后,测试的结果将更改为 “没有结论”。
Assert.Inconclusive("无法验证不返回值的方法。");
注释掉此 Assert 语句。
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”方法的精确性。
在“测试结果”窗口的 “结果”列中,当测试运行时,测试状态将显示为 “正在运行”。 测试运行完成后,测试的结果将更改为 “未通过”。
这将打开“测试结果详细信息”页,其中显示以下错误消息:“Assert.AreEqual 失败。预期值 <0.77> 和实际值 <23.21> 之间的差不应大于 <0.05>”。这些数字似乎表明数学运算不正确。由于 “BankAccountTest”类的 “DebitTest”方法测试 “BankAccount”类的 “Debit”方法,所以从检查 “Debit”方法开始。
m_balance += amount;
此赋值向余额增加金额,在 “Debit”方法中,应当减去赋值。 将此行更改为:
m_balance -= amount;
再次运行 “Debit”测试。
在“测试结果”窗口的 “结果”列中,将为 “DebitTest”显示 “已通过”。
|
更改源代码后不必重新生成测试项目,因为运行测试时会生成项目而不进行提示。 |
您创建了一个可以运行的单元测试,并通过它查找和修复了代码中的错误。
随即出现 “创建单元测试”对话框。
在显示的树结构中,只有 “FreezeAccount()”方法处于选定状态。
此时将新建一个名为 Bank.accessor 的专用访问器文件。该文件中包含特殊的访问器方法,测试使用这些方法间接调用 BankAccount 类中的私有方法。在解决方案资源管理器的“测试引用”文件夹中可以看到这个新文件。
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.");
}
运行 “FreezeAccountTest”测试。
在“测试结果”窗口的 “结果”列中,最终测试状态显示为 “已通过”。 该结果与预期结果一致,原因是测试在调用 “FreezeAccount()”方法冻结帐户之后调用了 “Credit()”方法。
您已经添加了一个私有方法,为其创建了新的单元测试并运行了该测试。可以对 balance 变量使用其他边界值(如 15.00)来多次运行该测试。
对程序集中的代码运行测试时,可以通过收集代码覆盖率数据来查看正在测试的项目代码部分。
随即出现 “测试设置”对话框。
运行这两个测试。
随即打开 “代码覆盖率结果”窗口。
将打开 Class1.cs 源代码文件并定位到 Debit 方法所在的位置。在此文件中,可以看到代码突出显示效果。用浅蓝色突出显示的代码行已在测试运行中执行过,用浅褐色突出显示的代码行已部分执行过,而用红褐色突出显示的代码行则还没有执行过。可以通过滚动查看此文件中其他方法的覆盖率。
如果在步骤 7 中选中了 TestProject1.dll 的复选框,则可以打开 Class1Test.cs(即包含单元测试的源代码文件)查看执行过的测试方法。其中应用了相同的突出显示方案:浅蓝色指示已执行的代码;浅褐色指示已部分执行的代码路径,红褐色指示测试运行时未经过的代码路径。