本演练将引导您完成创建、 运行和自定义一系列单元测试的托管的代码和 Visual Studio 测试浏览器使用 Microsoft 单元测试框架。 您将从正处于开发过程中的 C# 项目开始,创建执行该项目代码的测试,运行测试并检查结果。 然后,可以更改项目代码并重新运行测试。
本主题包含以下各节:
说明 |
---|
本演练使用 Microsoft 单元测试框架,用于托管代码。 测试资源管理器还可以从运行测试第三方设备有适配器测试资源管理器的测试框架。 有关更多信息,请参见 |
说明 |
---|
有关如何从命令行运行测试的信息,请参见。 |
-
Bank 项目。 请参见。
准备演练
-
打开 Visual Studio 2012。
-
在“文件”菜单上指向“新建”,然后单击“项目”。
此时将出现“新建项目”对话框。
-
在“已安装的模板”下单击“Visual C#”。
-
在应用程序类型的列表中单击“类库”。
-
在“名称”框中键入 Bank,然后单击“确定”。
说明 如果名称“Bank”已被使用,请为该项目选择其他名称。
将创建新的 Bank 项目并将其显示在解决方案资源管理器中,而且将在代码编辑器中打开 Class1.cs 文件。
说明 如果代码编辑器中未打开 Class1.cs 文件,请在解决方案资源管理器中双击文件 Class1.cs 将其打开。
-
从中复制源代码。
-
用中的代码替换 Class1.cs 的原始内容。
-
将文件另存为 BankAccount.cs
-
在“生成”菜单上,单击“生成解决方案”。
现在您有一个名为“Bank”的项目。 它包含要测试的源代码和用于对该源代码进行测试的工具。 Bank 的命名空间“BankAccountNS”包含公共类“BankAccount”,在以下过程中将对该类的方法进行测试。
在本快速入门,我们致力于Debit方法。将借记调用方法时资金总额中提取帐户,其中包含下面的代码:
// method under testpublic void Debit(double amount){ if(amount > m_balance) { throw new ArgumentOutOfRangeException("amount"); } if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount;}
系统必备:按照过程中的步骤执行操作。
若要创建单元测试项目
-
在文件 菜单中,选择 添加,然后选择 新项目....
-
在新建项目对话框中,展开已安装,展开 Visual C#,然后选择 测试。
-
从模板列表中,选择单元测试项目。
-
在名称 框中,输入 BankTest,然后再选择 确定。
BankTests 项目添加到银行解决方案。
-
在 BankTests 项目,将引用添加到银行解决方案。
在解决方案资源管理器中,选择引用 在 BankTests 项目,然后选择添加引用...从上下文菜单。
-
在引用管理器对话框中,展开解决方案 ,然后检查 银行项。
我们需要测试类验证BankAccount类。 我们可以使用 UnitTest1.cs 所生成的项目模板,但我们应该向提供的文件和类更具描述性的名称。 为此,我们在一个步骤中通过重命名解决方案资源管理器中的文件。
重命名一个类文件
在解决方案资源管理器中,选择 BankTests 项目中的 UnitTest1.cs 文件。 从上下文菜单中,选择重命名,然后再将文件重命名为 BankAccountTests.cs。 选择是在对话框,询问您是否要重命名项目中的所有引用的代码元素 UnitTest1。 此步骤将更改的类的名称BankAccountTest。
现在的 BankAccountTests.cs 文件中包含以下代码:
// unit test codeusing System;using Microsoft.VisualStudio.TestTools.UnitTesting;namespace BankTests{ [TestClass] public class BankAccountTests { [TestMethod] public void TestMethod1() { } }}
使用添加到测试项目中的语句
我们还可以添加 using 语句,以使我们能够调用到测试项目不使用完全限定名称的类。在类文件的顶部,添加:
using BankAccountNS
测试类要求
测试类的最低要求如下所示:
-
[TestClass]属性需要 Microsoft 单元测试框架的托管代码中的任何类的包含要测试的资源管理器中运行单元测试方法。
-
每个所需的测试资源管理器中运行的测试方法必须与修饰[TestMethod]属性。
您可以在不带有单元测试项目中的帮助器类[TestMethod],并可以帮助器方法不使用修饰的测试类[TestClass]。在测试方法中,您可以使用这些非修饰类和方法,但它们不会自动运行的测试资源管理器。
在此过程中,我们将编写单元测试以验证行为的方法Debit方法的BankAccount类。 上面列出的方法。
通过分析测试的方法,我们确定有三个需要检查的行为:
-
该方法将引发[ArgumentOutOfRangeException]如果贷方金额大于余额。
-
它也会引发ArgumentOutOfRangeException如果贷方金额小于零。
-
如果在签入时 1。)和 2。都满足时,该方法中减去从帐户余额的金额。
在我们第一次测试,我们验证有效的金额 (一个帐户余额小于它的大于零) 的帐户撤消量。
若要创建一个测试方法
-
使用添加BankAccountNS; BankAccountTests.cs 文件的语句。
-
下面的方法,加上BankAccountTests类:
// unit test code[TestMethod]public void Debit_WithValidAmount_UpdatesBalance(){ // arrange double beginningBalance = 11.99 double debitAmount = 4.55; double expected = 7.44; BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance); // act account.Credit(debitAmount); // assert double actual = account.Balance; Assert.AreEqual(expected, actual, 0.001, "Account not credited correctly");}
该方法很简单。 我们建立新的BankAccount具有期初余额的对象,然后撤消有效量。 我们使用 Microsoft 单元测试框架的托管代码方法来验证期末余额是否是我们所期望的结果。
测试方法要求
测试方法必须满足以下要求:
-
该方法必须与修饰[TestMethod]属性。
-
该方法必须返回 void。
-
该方法不能具有参数。
生成并运行测试
-
在“生成”菜单上,选择“生成解决方案”。
如果没有任何错误,UnitTestExplorer 窗口将显示与 Debit_WithValidAmount_UpdatesBalance 中列出不运行测试组。 如果生成成功后没有测试资源管理器,请选择测试 的菜单上,然后选择 Windows,然后选择 测试资源管理器。
-
选择运行所有运行测试。 测试运行状态栏位于窗口的顶部会呈现动画效果。 在测试运行结束时,条形图将通过所有测试方法,如果绿色或红色如果任何测试失败。
-
在这种情况下,测试就不会失败。 测试方法移动到失败测试。 组。 测试以查看该窗口底部的详细信息的资源管理器中选择的方法。
分析测试结果
测试结果包含一条消息,描述该故障。 为AreEquals方法,消息显示您预期的 ((预期 <XXX>参数) 和实际接收到的内容 ( 实际 <YYY>参数)。 我们想要拒绝的期初余额的余额,但而增加了提款量。
Reexamination 的借方代码显示的单元测试已成功在中查找错误。 提款量添加到帐户余额时应减去该值。
更正错误
若要更正该错误,只需替换行
m_balance += amount;
with
m_balance -= amount;
重新运行测试
测试资源管理器中选择运行所有重新运行测试。红/绿栏变为绿色,并测试被移到传递测试组。
本部分介绍一个循环往复的过程的分析、 单元测试开发和重构功能可以怎样帮助您使生产代码更可靠,更有效。
分析问题
创建测试方法以确认后有效的金额是否正确扣除的Debit方法,我们就可以把其余的情况下要在我们的原始分析:
-
该方法将引发ArgumentOutOfRangeException如果贷方金额大于余额。
-
它也会引发ArgumentOutOfRangeException如果贷方金额小于零。
创建测试方法
还有的首次尝试创建一个测试方法来解决这些问题:
//unit test method[TestMethod][ExpectedException(typeof(ArgumentOutOfRangeException))]public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange(){ // arrange double beginningBalance = 11.99; double debitAmount = -100.00; BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance); // act account.Debit(debitAmount); // assert is handled by ExpectedException}
我们使用属性声明引发适当的异常。该属性将导致测试失败,除非ArgumentOutOfRangeException引发。运行测试,与这两种正、 负debitAmount值,然后暂时修改引发一般测试的方法时量小于零演示该测试是否正常。若要测试这种情况,已撤消的金额大于余额时,我们要做的只是:
-
创建新测试方法名为Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange。
-
将复制的方法体,从Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange的新方法。
-
设置debitAmount大于余额为数字。
运行测试
运行这两种方法使用不同的值为debitAmount测试足以应付我们剩余的情况下进行演示。运行所有三项测试确认正确覆盖所有情况下,在我们的原始分析。
继续分析
但是,最后两个测试方法都还有些人困惑。我们无法确定任何一个测试运行时,将引发测试代码中的条件。区分两种情况的某些方面会很有帮助。正如我们考虑的问题更多,它将成为明显知道发生冲突的情况可能会增加我们的测试中的信心。此信息也很有可能会比较有帮助的生产机制处理时它将通过测试的方法引发的异常。所有相关,有助于方法引发时生成的详细信息,但ExpectedException属性不能提供此信息。.
我们再次查看该测试方法,请参阅使用这两个条件语句ArgumentOutOfRangeException的名称作为参数的参数的构造函数:
throw new ArgumentOutOfRangeException("amount");
从 MSDN 库中的搜索,我们发现一个构造函数存在的报告更丰富的信息。 ArgumentOutOfRangeException (String, Object, String)包括的参数,该参数值,而用户定义的消息的名称。我们可以使用此构造函数所测试的方法进行重构。更好的是,我们可以使用公开的类型成员指定的错误。
重构测试代码
首先,我们定义两个常量的类范围内的错误消息:
// class under testpublic const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";public const string DebitAmountLessThanZeroMessage = "Debit amount less than zero";
然后,我们修改两个条件语句中的Debit方法:
// method under test// ... if (amount > m_balance) { throw new ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage); } if (amount < 0) { throw new ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage); }// ...
重构的测试方法
在我们的测试方法,我们首先删除ExpectedException属性。它的位置,我们捕捉引发的异常,并验证在正确的条件语句中引发了它。但是,我们现在必须决定两个选项以验证我们剩余的条件。例如在Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange方法,我们可以执行下列操作之一:
-
断言的ActualValue异常的属性 (第二个参数的ArgumentOutOfRangeException构造函数) 大于期初余额。此选项要求我们测试ActualValue属性对异常的beginningBalance变量的测试方法,并且还要求,请验证ActualValue大于零。
-
断言 (第三个参数的构造函数) 的消息包括DebitAmountExceedsBalanceMessage中定义BankAccount类。
Microsoft 单元测试框架中的方法,使我们能够验证第二个选项,而无需计算所需的第一个选项。
在修改第二次尝试Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange可能类似于:
[TestMethod]public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange(){ // arrange double beginningBalance = 11.99; double debitAmount = 20.0; BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\ // act try { account.Debit(debitAmount); } catch (ArgumentOutOfRangeException e) { // assert StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage); }}
重新测试、 重写,和重新分析
当我们重新测试的测试方法,使用不同的值时,我们遇到了以下事实:
-
如果使用,我们将捕获正确的错误debitAmount大于余额的Contains断言刀路,将忽略该异常,并因此通过了测试方法。这是我们所需的行为。
-
如果我们使用debitAmount、 断言失败,因为返回不正确的错误信息。如果我们将介绍一种临时,断言同样无法正常工作ArgumentOutOfRange上的另一点的方法在测试代码路径中的异常。这也是很好。
-
如果debitAmount的值是有效 (我.e.、 小于但大于 0,未捕获异常时,因此永远不会捕获到该断言的平衡。通过的测试方法。这不是很好,因为我们希望测试方法失败不会引发异常。
第三个事实是我们的测试方法中的错误。若要解决此问题,我们将添加断言来处理,不会引发异常的情况下测试方法的末尾。
但是,重新测试显示测试现在是否捕捉到正确的异常,则失败。Catch 语句将重置该异常,该方法将继续执行,在新的断言失败。若要解决此新问题,我们将添加return语句之后StringAssert。重新测试,确认我们已解决我们的问题。我们的最终版本的Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange看上去像下面:
[TestMethod]public void Debit_WhenAmountIsGreaterThanBalance_ShouldThrowArgumentOutOfRange(){ // arrange double beginningBalance = 11.99; double debitAmount = 20.0; BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);\ // act try { account.Debit(debitAmount); return; } catch (ArgumentOutOfRangeException e) { // assert StringAssert.Contains(e.Message, BankAccount. DebitAmountExceedsBalanceMessage); } Assert.Fail("No exception was thrown.")}
中的最后一节,我们做的改进我们的测试代码的工作带来更强健和信息性的测试方法。但更重要的是,额外的分析还带来更好的代码,我们所测试的项目中。