使用 Visual Studio 2005 Team System 進(jìn)行單元測(cè)試并生成用于 Unit Test Framework 的源代碼
發(fā)布日期: 11/10/2005 | 更新日期: 11/10/2005
Scott Dockendorf
Telligent Systems, Inc.
適用于:
Microsoft Visual Studio 2005 Team System Beta 2
Team Architect & Team Test Editions
Microsoft Visual C# 2005
摘要: Scott 詳細(xì)介紹自動(dòng)化單元測(cè)試的基本內(nèi)容,以及由 Microsoft Visual Studio 2005 Team System 提供的 Unit Test Framework 中包含的代碼生成引擎。
本頁(yè)內(nèi)容
簡(jiǎn)介
隨著業(yè)務(wù)的革新和發(fā)展,運(yùn)行它們的系統(tǒng)也需要進(jìn)行更新。隨業(yè)務(wù)的發(fā)展、革新以及與合作伙伴、客戶(hù)和供應(yīng)商的結(jié)合,這些系統(tǒng)將在復(fù)雜性方面持續(xù)擴(kuò)增。
這種復(fù)雜性迫使 IT 的領(lǐng)導(dǎo)者們?cè)陂_(kāi)發(fā)過(guò)程中(即,在實(shí)現(xiàn)之前)確保質(zhì)量。有一種方法可使開(kāi)發(fā)人員減少進(jìn)入 QA 環(huán)節(jié)的故障數(shù)量,即,針對(duì)自定義代碼嚴(yán)格執(zhí)行自動(dòng)化單元測(cè)試。在開(kāi)發(fā)過(guò)程中強(qiáng)制使用自動(dòng)化單元測(cè)試可為團(tuán)隊(duì)成員提供有關(guān)如何使用自定義代碼的示例(這些示例易于使用并自行記錄)。
使用結(jié)構(gòu)化、自動(dòng)化單元測(cè)試面臨的挑戰(zhàn)之一是完成這些任務(wù)所需的代碼總數(shù)。(測(cè)試代碼需要使用大量代碼!)代碼生成的概念(簡(jiǎn)單定義為“創(chuàng)建軟件的軟件”)正隨著時(shí)間的快速推移而逐漸深入到團(tuán)隊(duì) IT 開(kāi)發(fā)之中。有些人認(rèn)為代碼生成有助于縮短“推向市場(chǎng)”策略的時(shí)間,強(qiáng)制內(nèi)部標(biāo)準(zhǔn)/協(xié)定,并促進(jìn)開(kāi)發(fā)過(guò)程。
Microsoft 認(rèn)識(shí)到這一需要后提供了一個(gè)功能豐富、帶有下一代開(kāi)發(fā)平臺(tái) Visual Studio 2005 Team System (VSTS) 的代碼生成引擎。本文提供針對(duì)單元測(cè)試代碼生成的循序漸進(jìn)的指導(dǎo),并深入探討如何在用例中使用。
重新思考單元測(cè)試
請(qǐng)考慮以下情況:您負(fù)責(zé)為公司生成下一代系統(tǒng),同時(shí)您是較大的開(kāi)發(fā)團(tuán)隊(duì)中的一員。您是 UI 開(kāi)發(fā)人員,負(fù)責(zé)盡可能多地創(chuàng)建 Microsoft ASP.NET/Microsoft WinForms。您依賴(lài)“中間層”團(tuán)隊(duì)完成其中間層組件 — 這些組件用于執(zhí)行數(shù)據(jù)庫(kù) CRUD (Create-Retrieve-Update-Delete) 以及與該系統(tǒng)中每個(gè)實(shí)體相關(guān)的業(yè)務(wù)規(guī)則。
經(jīng)過(guò)幾周的 UI 開(kāi)發(fā),您完成了窗體并且收到了中間層開(kāi)發(fā)人員打算向您提交其類(lèi)庫(kù)的消息。表 1 提供一段對(duì)話示例,說(shuō)明我們大多數(shù)人在開(kāi)發(fā)過(guò)程中都會(huì)遇到的一些事情。
|
中間層:
|
“這些對(duì)象隨時(shí)供您使用 — 為此,只需獲取 OurSystemBL.dll 的最新版本。”
|
|
UI
:
|
“謝謝。您有供我們查看文檔嗎?”
|
|
中間層:
|
“哈哈!是的,當(dāng)然有!我們花了很多時(shí)間編寫(xiě)它!請(qǐng)查看 Design Document — 噢,請(qǐng)等一等,它還沒(méi)有完成……(不久之后即可完成!)”
|
|
UI
:
|
“您使用 XML 文檔了嗎?”
|
|
中間層:
|
“在構(gòu)造函數(shù)中,但許多方法都不使用。”
|
|
UI
:
|
“顯示如何創(chuàng)建、執(zhí)行并刪除對(duì)象的示例代碼,怎么樣?”
|
|
中間層:
|
“我已經(jīng)附加了一個(gè)示例 WinForms 應(yīng)用程序(從我的工作區(qū)),它應(yīng)該能夠提供一些您所需的內(nèi)容……,雖然它不在 Microsoft Visual SourceSafe 中。”
|
在考慮如何進(jìn)行這樣有趣的 項(xiàng)目之后,您打定了主意,決定檢驗(yàn)中間層的單元測(cè)試套件。在深入鉆研該代碼之后,您注意到該窗體有兩個(gè)未標(biāo)記的文本框,以及三個(gè)標(biāo)記為 button1、button2 和 button3 的按鈕(幸運(yùn)的話,它們將排列在窗體上)。接下來(lái),在查看與這些按鈕相關(guān)的事件之后,您認(rèn)識(shí)到這些代碼都未經(jīng)注釋?zhuān)⑶覕?shù)據(jù)變量都被命名為 x、y、z。如果幸運(yùn),您還會(huì)注意到 button1 和 button2 執(zhí)行該對(duì)象的 Save() 方法,而 button3 執(zhí)行 Delete() 方法。執(zhí)行時(shí),您會(huì)接收到很多 System.Exception 錯(cuò)誤,這是因?yàn)檫z漏了很多配置設(shè)置。
這顯然是一個(gè)特例,我希望多數(shù)開(kāi)發(fā)團(tuán)隊(duì)不要進(jìn)行這一試驗(yàn),下面讓我們看一下該方案中“單元測(cè)試”遇到的問(wèn)題:
| ? |
這種形式的單元測(cè)試代碼不是結(jié)構(gòu)化的:代碼充斥到按鈕單擊事件中并且難以編譯。
|
| ? |
這種形式的單元測(cè)試代碼記錄得不太好。
|
| ? |
這種形式的單元測(cè)試并不基于“已知”為好或壞的數(shù)據(jù) — 它完全依賴(lài)于輸入到那些未標(biāo)記的文本框的內(nèi)容。
| ? |
單元測(cè)試代碼不能自動(dòng)重復(fù),它基于輸入的代碼。
|
| ? |
單元測(cè)試代碼覆蓋是未知的 — 用數(shù)據(jù)指示實(shí)際測(cè)試的代碼量。
|
|
| ? |
實(shí)現(xiàn)的詳細(xì)信息不易于在團(tuán)隊(duì)成員間進(jìn)行傳播。
|
輸入自動(dòng)化單元測(cè)試
xUnit 框架在 1998 年作為 eXtreme 編程的核心概念引入。它提出了一個(gè)有效的機(jī)制,有助于開(kāi)發(fā)人員將結(jié)構(gòu)化、有效且自動(dòng)的單元測(cè)試添加常規(guī)開(kāi)發(fā)活動(dòng)中。從那以后,該框架演化為針對(duì)自動(dòng)化單元測(cè)試框架的實(shí)際標(biāo)準(zhǔn)。
創(chuàng)建自動(dòng)化單元測(cè)試的用例
簡(jiǎn)單說(shuō),自動(dòng)化單元測(cè)試是:
| ? |
結(jié)構(gòu)化的。
|
| ? |
自行記錄的。
|
| ? |
自動(dòng)且可重復(fù)的。
|
| ? |
基于已知數(shù)據(jù)。
|
| ? |
旨在測(cè)試積極和消極操作。
|
| ? |
非常適合跨不同計(jì)算機(jī)的測(cè)試實(shí)現(xiàn)。
|
| ? |
配置、實(shí)現(xiàn)和執(zhí)行的示例。
|
xUnit 框架元素
表 2 分析 xUnit 框架以及對(duì)應(yīng)于 Visual Studio 2005 Team System 的 Unit Testing Framework 等價(jià)物的基本概念。
|
測(cè)試
|
TestMethod
|
簡(jiǎn)單說(shuō),這些是您的測(cè)試。測(cè)試預(yù)期結(jié)果的邏輯,并報(bào)告未取得結(jié)果(如果有)。請(qǐng)將它看作您的“方法”。
|
|
測(cè)試裝置
|
TestClass
|
針對(duì)大量測(cè)試的一個(gè)邏輯分組。請(qǐng)將它看作您的“類(lèi)”。
|
|
測(cè)試套件
|
測(cè)試列表 **
|
針對(duì)大量測(cè)試裝置的一個(gè)邏輯分組。請(qǐng)將它看作您的“類(lèi)庫(kù)”。
注不需要一個(gè)屬性。
|
|
測(cè)試運(yùn)行器
|
VS 2005 VSTS Unit Testing Framework
|
GUI/Console 應(yīng)用程序負(fù)責(zé)發(fā)現(xiàn)、執(zhí)行和報(bào)告測(cè)試結(jié)果。Visual Studio 2005 Team System 將作為本文的測(cè)試運(yùn)行器。
|
測(cè)試裝置示例
請(qǐng)考慮以下針對(duì) BankAccount 類(lèi)的類(lèi)關(guān)系圖,以及一個(gè)示例測(cè)試裝置 (BankAccountTests.cs)。
圖
1. BankAccount
類(lèi)
示例測(cè)試裝置: BankAccountTests.cs
using BankAccountDemo.Business;
using Microsoft.VisualStudio.QualityTools.UnitTesting.Framework;
namespace BankAccountDemo.Business.Tests
{
[TestClass()]
public class BankAccountTest
{
[TestInitialize()]
public void Initialize() {
}
[TestCleanup()]
public void Cleanup() {
}
[TestMethod()]
public void ConstructorTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
Assert.AreEqual(currentBalance, target.CurrentBalance,
"Balances are not equal upon creation");
}
[TestMethod()]
public void DepositMoneyTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
float depositAmount = 10;
target.DepositMoney(depositAmount);
Assert.IsTrue( (currentBalance + depositAmount) >
target.CurrentBalance,
"Deposit not applied correctly");
}
[TestMethod()]
public void MakePaymentTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
float paymentAmount = 250;
target.MakePayment(paymentAmount);
Assert.IsTrue(currentBalance - paymentAmount ==
target.CurrentBalance,
"Payment not applied correctly");
}
}
}
主單元測(cè)試概念 == 斷言
用于該形式單元測(cè)試的主要概念是,自動(dòng)化單元測(cè)試是基于“斷言”的,即可定義為“事實(shí)或您相信為事實(shí)的內(nèi)容”。從邏輯角度看,請(qǐng)考慮該語(yǔ)句“when I do {x}, I expect {y} as a result”。
這可以輕松地翻譯為代碼,方法是使用 Microsoft.VisualStudio.QualityTools.UnitTesting.Framework 命名空間中可用的三個(gè)“斷言”類(lèi)中的任一個(gè):Assert、StringAssert 和 CollectionAssert。主類(lèi) Assert 提供用于測(cè)試基礎(chǔ)條件語(yǔ)句的斷言。StringAssert 類(lèi)自定義了在使用字符串變量時(shí)有用的斷言。同樣,CollectionAssert 類(lèi)包括在使用對(duì)象集合時(shí)有用的斷言方法。
表 3 顯示可用于當(dāng)前版本 Unit Testing Framework 的斷言。
AreEqual()
AreNotEqual()
AreNotSame()
AreSame()
EqualsTests()
Fail()
GetHashCodeTests()
Inconclusive()
IsFalse()
IsInstanceOfType()
IsNotInstanceOfType()
IsNotNull()
IsNull()
IsTrue()
|
Contains()
DoesNotMatch()
EndsWith()
Matches()
StartsWith()
|
AllItemsAreInstancesOfType()
AllItemsAreNotNull()
AllItemsAreUnique()
AreEqual()
AreEquivalent()
AreNotEqual()
AreNotEquivalent()
Contains()
DoesNotContain()
IsNotSubsetOf()
IsSubsetOf()
|
這些自動(dòng)化單元測(cè)試用什么運(yùn)行?
正如前面提到的,xUnit 框架將“測(cè)試運(yùn)行器”的概念定義為應(yīng)用程序負(fù)責(zé):(a) 執(zhí)行單元測(cè)試;(b) 報(bào)告測(cè)試結(jié)果。對(duì)于本文,包含 Visual Studio 2005 Team System (VSTS) 的 Unit Testing 引擎作為我們的“測(cè)試運(yùn)行器”。圖 2 表示 BankAccountTests.cs 類(lèi)的執(zhí)行結(jié)果。
圖
2.
測(cè)試結(jié)果窗格:?jiǎn)卧獪y(cè)試執(zhí)行結(jié)果
Microsoft Visual Studio 2005 使用源項(xiàng)目的代碼模型動(dòng)態(tài)填充該視圖。它基于該源代碼中的自定義屬性動(dòng)態(tài)發(fā)現(xiàn)有關(guān)該測(cè)試套件的信息。表 4 表示最常見(jiàn)的單元測(cè)試屬性(以及執(zhí)行的次序)。
|
TestClass()
|
該屬性表示一個(gè)測(cè)試裝置。
|
|
TestMethod()
|
該屬性表示一個(gè)測(cè)試用例。
|
|
AssemblyInitialize()
|
在執(zhí)行為執(zhí)行選擇的第一個(gè) TestClass() 中的第一個(gè) TestMethod() 之前,執(zhí)行帶有該屬性的方法。
|
|
ClassInitialize()
|
帶有該屬性的方法在執(zhí)行第一個(gè)測(cè)試之前調(diào)用。
|
|
TestInitialize()
|
帶有該屬性的方法在執(zhí)行每個(gè) TestMethod() 之前調(diào)用。
|
|
TestCleanup()
|
帶有該屬性的方法在執(zhí)行每個(gè) TestMethod() 之后調(diào)用。
|
|
ClassCleanup()
|
帶有該屬性的方法在執(zhí)行 ALL 測(cè)試之后調(diào)用。
|
|
AssemblyCleanup()
|
在執(zhí)行為執(zhí)行選擇的第一個(gè) TestClass() 中的第一個(gè) TestMethod() 之后,執(zhí)行帶有該屬性的方法。
|
|
Description()
|
提供關(guān)于給定 TestMethod() 的描述。
|
|
Ignore()
|
由于某種原因忽略 TestMethod() 或 TestClass()。
|
|
ExpectedException()
|
當(dāng)測(cè)試特定異常時(shí),如果使用該屬性指定的異常不是從實(shí)現(xiàn)代碼引發(fā),則測(cè)試不會(huì)失敗。
|
我編寫(xiě)什么類(lèi)型的測(cè)試?
一個(gè)方法及其相關(guān)測(cè)試之間很難有一對(duì)一關(guān)系。編寫(xiě)自動(dòng)化單元測(cè)試需要開(kāi)發(fā)人員“進(jìn)行全面思考”,并了解關(guān)于對(duì)象的所有內(nèi)容 — 它將如何消耗、使用、處理,以及在任何情況下如何起到積極、消極、非決定性作用。
例如,請(qǐng)考慮一個(gè)用于針對(duì)數(shù)據(jù)庫(kù)中 Customer 項(xiàng)執(zhí)行 CRUD(創(chuàng)建、檢索、更新、刪除)功能的典型對(duì)象方法。對(duì)于該對(duì)象的 Load() 方法,要針對(duì)以下方案編寫(xiě)測(cè)試:
| ? |
構(gòu)造函數(shù)測(cè)試 — 確保對(duì)象正確加載,帶有正確的信息。
|
| ? |
PositiveLoadScalarTest — 測(cè)試數(shù)據(jù)庫(kù)中一個(gè) Customer 的成功加載。
|
| ? |
NegativeLoadScalarTest — 測(cè)試一個(gè) Customer 的失敗加載,即該 Customer 不在數(shù)據(jù)庫(kù)中。
|
| ? |
PositiveLoadTest — 基于已知數(shù)據(jù)測(cè)試 Customer 的成功加載。
|
| ? |
NegativeLoadTest — 測(cè)試數(shù)據(jù)庫(kù)中不存在的 Customer 的失敗加載。
|
| ? |
NegativeValidationTest — 確保驗(yàn)證邏輯正確工作。
|
這些只是自動(dòng)化單元測(cè)試套件許多用法中的一部分。我曾經(jīng)聽(tīng)說(shuō)一個(gè)小團(tuán)隊(duì)使用單元測(cè)試查看針對(duì)其組件的已知安全攻擊。從宏觀的角度來(lái)看,單元測(cè)試應(yīng)該明確保證組件的正常使用。具有豐富的測(cè)試集將使團(tuán)隊(duì)確信您已經(jīng)準(zhǔn)確實(shí)現(xiàn)了既定的目標(biāo):編寫(xiě)有效的軟件。無(wú)論自信源自哪里 — 這就是您需要編寫(xiě)的測(cè)試。
您測(cè)試什么?
從本質(zhì)上看,這些自動(dòng)化單元測(cè)試非常低級(jí)。它們旨在測(cè)試下至構(gòu)造函數(shù)、方法調(diào)用的對(duì)象,甚至是對(duì)象上的屬性。
關(guān)于“公共對(duì)私有”的主題在單元測(cè)試派系中引發(fā)了許多爭(zhēng)論。許多人認(rèn)為單元測(cè)試只應(yīng)該測(cè)試對(duì)象的公共接口。其他人認(rèn)為應(yīng)該測(cè)試每個(gè)調(diào)用 — 包括內(nèi)部私有方法。VSTS 支持兩個(gè)單元測(cè)試級(jí)別。VSTS 通過(guò)使用私有訪問(wèn)器或包裝類(lèi)支持私有測(cè)試,后者提供基于“私有”方法和屬性生成單元測(cè)試的功能。
為什么生成代碼?
閱讀上面的列表后,您可能會(huì)想起前面項(xiàng)目的單個(gè)對(duì)象,并思考:“如果我用“這些”對(duì)象進(jìn)行該操作,就需要編寫(xiě)大量代碼!”請(qǐng)考慮開(kāi)發(fā)人員仍編寫(xiě)“單元測(cè)試”代碼的事實(shí) — 只在不同的窗體(例如,前面提到的 WinForms 示例)上進(jìn)行。此外,具有可自行記錄、可重用的實(shí)現(xiàn)示例帶來(lái)的好處遠(yuǎn)大于生成更多代碼所帶來(lái)的麻煩。最后,在單元測(cè)試中設(shè)計(jì)更多的環(huán)節(jié)已證明可以減少質(zhì)量保證環(huán)節(jié)中的故障。
正如前面所提到的,代碼生成是“軟件創(chuàng)建軟件”的過(guò)程。基于可重復(fù)的過(guò)程創(chuàng)建代碼是理想的。例如,一些使用代碼生成的較好示例包括:腳本數(shù)據(jù)、創(chuàng)建表示實(shí)體及其在儲(chǔ)存庫(kù)(數(shù)據(jù)庫(kù) CRUD)中存在的對(duì)象,或者創(chuàng)建適用于數(shù)據(jù)維護(hù)的 UI 控件。使用代碼生成的好處包括:
| ? |
節(jié)省時(shí)間 — 為什么花幾小時(shí)/天/周創(chuàng)建一些在幾秒/分鐘內(nèi)就可以創(chuàng)建的內(nèi)容?
|
| ? |
強(qiáng)制標(biāo)準(zhǔn)
/
約定
— 對(duì)于強(qiáng)制您的標(biāo)準(zhǔn)和命名約定而言,沒(méi)有什么比消除開(kāi)發(fā)中的人員因素并依賴(lài)基于“您的”規(guī)則的可重復(fù)過(guò)程更好的了。
|
| ? |
測(cè)試私有方法的功能 — 正如本文前面提到的,Unit Testing Framework 提供使用“私有訪問(wèn)器”類(lèi)測(cè)試私有方法的功能。該代碼生成引擎創(chuàng)建與這些訪問(wèn)器類(lèi)相關(guān)的所有“基礎(chǔ)代碼”。
|
| ? |
獲取現(xiàn)有組件的信息 — 搜索另一個(gè)開(kāi)發(fā)人員的組件嗎?基于這些組件生成代碼可能提供關(guān)于該實(shí)現(xiàn)以及該對(duì)象接口的簡(jiǎn)明示例。此外,進(jìn)行設(shè)計(jì)并在編碼之前“清除”其對(duì)象的公共接口(例如,通過(guò)使用 VS 2005 類(lèi)設(shè)計(jì)器)的開(kāi)發(fā)人員將極大地受益于代碼生成引擎。
|
正如您所預(yù)期的,自動(dòng)化單元測(cè)試屬于“優(yōu)秀代碼生成候選者”。在現(xiàn)有組件中指出一些內(nèi)容并針對(duì)這些自動(dòng)單元測(cè)試生成初始代碼難道不是很理想嗎?而且不只是保留單元測(cè)試框架,還會(huì)圍繞對(duì)象的公共接口生成實(shí)現(xiàn)示例嗎?將來(lái)的 Visual Studio 2005 Team System 用戶(hù)將擁有該功能以及更多功能!
讓我們生成一些代碼吧!
本例中,我們將生成本文前面提到的 BankAccount 類(lèi)的代碼。本文的這一部分旨在為您介紹代碼生成過(guò)程,并重點(diǎn)介紹所提供的功能以及從 VSTS 使用 Unit Testing 引擎的好處。
第 1 步:創(chuàng)建您的實(shí)現(xiàn)代碼
首先,我們創(chuàng)建一個(gè)將用作應(yīng)用程序的業(yè)務(wù)層的類(lèi)庫(kù)項(xiàng)目。
要在 VS 2005 中創(chuàng)建該庫(kù):
|
1.
|
啟動(dòng) Visual Studio 2005 Beta 2。
|
|
2.
|
單擊 File | New | Project。
|
|
3.
|
選擇您選定的語(yǔ)言 Windows,并選擇 Class Library 項(xiàng)目模板。
|
|
4.
|
將 Name 和 Solution Name 設(shè)置為 BankAccountDemo.Business,選擇一個(gè)位置,并單擊 OK 來(lái)創(chuàng)建該類(lèi)庫(kù)。
|
VS 2005 創(chuàng)建該類(lèi)后,下一個(gè)任務(wù)就是創(chuàng)建針對(duì)您的項(xiàng)目設(shè)計(jì)的 BankAccount 類(lèi)。為此,需要執(zhí)行以下操作:
|
1.
|
在解決方案資源管理器中單擊右鍵,并單擊 Delete,從項(xiàng)目中移除該文件并將其從硬盤(pán)中刪除。
|
|
2.
|
右鍵單擊 BankAccountDemo.Business 項(xiàng)目,然后單擊 Add,之后單擊 Class。
|
|
3.
|
選擇文件名 BankAccount.cs,并單擊 Add 創(chuàng)建類(lèi)文件。
|
|
4.
|
針對(duì) BankAccount.cs 文件對(duì)代碼進(jìn)行以下更改。
|
using System;
using System.Collections.Generic;
using System.Text;
namespace BankAccountDemo.Business
{
public class BankAccount
{
// Properties
private float _currentBalance;
public float CurrentBalance
{
get { return _currentBalance; }
}
// Constructors
public BankAccount(float initialBalance)
{
this._currentBalance = initialBalance;
}
// Methods
public void DepositMoney(float depositAmount)
{
this._currentBalance += depositAmount;
}
public void MakePayment(float paymentAmount)
{
this._currentBalance -= paymentAmount;
}
}
}
第 2 步:生成您的初始單元測(cè)試代碼
由于 Unit Testing 引擎內(nèi)置于 Visual Studio 2005 Team System,因此生成代碼比以前更容易。除了生成單元測(cè)試結(jié)構(gòu)之外,它將生成特定于實(shí)例的信息,例如,對(duì)象創(chuàng)建、類(lèi)型化參數(shù)和方法執(zhí)行。
VS 2005 提供在任何類(lèi)結(jié)構(gòu)級(jí)別生成單元測(cè)試代碼的功能,這些級(jí)別包括命名空間、類(lèi)、方法、屬性、構(gòu)造函數(shù),等等。可通過(guò)右鍵單擊這些代碼元素并單擊 Generate test(s)(圖 3)進(jìn)行此操作。
圖
3. Generate test(s)
方法
因此,要開(kāi)始代碼生成過(guò)程,請(qǐng)執(zhí)行以下步驟:
| ? |
右鍵單擊該類(lèi)名 BankAccount 并單擊 Create Tests。
|
現(xiàn)在,應(yīng)該為您提供 Generate Unit Tests 對(duì)話框(如圖 4 所示)。該對(duì)話框及其組件提供對(duì)該過(guò)程中生成的代碼進(jìn)行自定義的功能。讓我們看一下所有這些元素。
圖
4.
生成
Unit Tests
對(duì)話框
Current selection: 樹(shù)視圖允許導(dǎo)航自定義類(lèi)及其元素。VS 2005 使用反射填充該樹(shù)視圖,并在右鍵單擊以及單擊 Create Tests 的位置自動(dòng)選擇組件。圖 3 中,由于我在類(lèi)級(jí)別進(jìn)行了此操作,因此該對(duì)話框自動(dòng)選擇用于代碼生成的所有類(lèi)元素。如果選擇在單個(gè)級(jí)別(即,構(gòu)造函數(shù)、屬性或方法)進(jìn)行生成,則只選擇那些元素。
Filter 選項(xiàng)(位于右上角)提供修改樹(shù)視圖(圖 5)中所示結(jié)果的功能,包括顯示非公共項(xiàng)、基類(lèi)型以及“只屬于我的代碼”。如果使用的是大型解決方案,或者感覺(jué)顯示私有的內(nèi)部結(jié)構(gòu)會(huì)弄亂選擇窗口,那么這對(duì)您很有益處。
圖
5.
篩選選擇結(jié)果
下一個(gè)是 Output project 對(duì)話框(位于 Current selection: 樹(shù)視圖下)。該列表框允許您針對(duì)生成的測(cè)試裝置選擇目的項(xiàng)目(圖 6)。如果您的解決方案包含以前創(chuàng)建的測(cè)試項(xiàng)目,則將包含該測(cè)試項(xiàng)目以供選擇。由于這是我們首次訪問(wèn)該對(duì)話框,因此可以選擇 Create a new … Test Project 選項(xiàng)。
圖
6.
輸出項(xiàng)目選擇
要繼續(xù)我們的過(guò)程,請(qǐng)執(zhí)行以下操作:
| ? |
基于您選擇的語(yǔ)言選擇 Create a new {0} Test Project。
|
最后,該對(duì)話框提供通過(guò) Settings 按鈕(位于左下角)自定義代碼生成過(guò)程的功能。單擊該按鈕將加載 Test Generation Settings 對(duì)話框,如圖 7 所示。
圖
7. Test Generation Settings
對(duì)話框
該對(duì)話框允許您進(jìn)行以下更改:
| ? |
更改命名約定,它們用于生成針對(duì)文件、類(lèi)(測(cè)試裝置)和方法(測(cè)試)的名稱(chēng)。
|
| ? |
打開(kāi)/關(guān)閉使所有測(cè)試結(jié)果在默認(rèn)情況下為 Inconclusive 的功能。選擇該選項(xiàng)將在每個(gè)生成的 Test() 方法中包含以下占位符語(yǔ)句。
Assert.Inconclusive("TODO: Implement code to verify target");
|
| ? |
打開(kāi)/關(guān)閉啟用生成警告的功能 — 即,如果在代碼生成過(guò)程中出現(xiàn)任何警告,都要進(jìn)行報(bào)告。
|
| ? |
在全局范圍內(nèi)限制所有類(lèi)型。該設(shè)置通知代碼生成引擎將一個(gè)全局限定符(在 Microsoft Visual C# 2005 中是 global::)添加到變量聲明中。當(dāng)在多個(gè)命名空間中具有名稱(chēng)相似的對(duì)象時(shí),請(qǐng)使用該設(shè)置。否則,代碼生成引擎將創(chuàng)建邏輯來(lái)創(chuàng)建該對(duì)象,但是編譯器不能確定創(chuàng)建哪個(gè)類(lèi),因此會(huì)產(chǎn)生錯(cuò)誤。
|
| ? |
啟用/禁用針對(duì)已具有測(cè)試的項(xiàng)生成測(cè)試的功能。下面我們將討論關(guān)于后續(xù)代碼生成嘗試的主題。
|
| ? |
啟用/禁用文檔注釋。這允許您在使用每個(gè) Test() 方法時(shí)禁用 XML 文檔的創(chuàng)建
|
要完成我們的配置并生成單元測(cè)試代碼(以及更多),請(qǐng)執(zhí)行以下操作:
|
1.
|
單擊 OK 按鈕開(kāi)始代碼生成過(guò)程。
|
|
2.
|
輸入名稱(chēng) BankAccountDemo.Business.Test 作為新項(xiàng)目名,并單擊 Create 按鈕完成該過(guò)程。
|
VS 2005 將顯示一個(gè)進(jìn)度欄,提供代碼生成過(guò)程中的狀態(tài)。該過(guò)程將在幾秒鐘內(nèi)完成,您可以看到一個(gè)名為 BankAccountTest.cs 的類(lèi)。
生成了什么?
在我們對(duì)該測(cè)試裝置進(jìn)行特別查看之前,讓我們看一下在代碼生成過(guò)程中創(chuàng)建了什么。
首先,它創(chuàng)建了 Test Class Library 項(xiàng)目 BankAccountDemo.Business.Test。請(qǐng)注意該項(xiàng)目如何包含對(duì)實(shí)現(xiàn)類(lèi) BankAccountDemo.Business(您從其中生成代碼)和 Microsoft.VisualStudio.QualityTools.UnitTestFramework 類(lèi)庫(kù)的引用。在查看該類(lèi)的內(nèi)容時(shí),您將注意到以下文件:
| ? |
AuthoringTests.txt — 這是一些信息性的內(nèi)容,定義如何使用單元測(cè)試(打開(kāi)、查看、運(yùn)行、查看結(jié)果、更改測(cè)試的運(yùn)行方式),以及 VSTS 中包含的不同測(cè)試類(lèi)型的定義。
|
| ? |
ManualTest1.mht — 這是 VSTS 中使用的手動(dòng)測(cè)試套件,用于執(zhí)行測(cè)試并報(bào)告結(jié)果。手動(dòng)測(cè)試是 VSTS 支持的一個(gè)附加測(cè)試類(lèi)型。有關(guān)更多信息,請(qǐng)參閱 MSDN 資源庫(kù)的“手動(dòng)測(cè)試”主題。
|
| ? |
UnitTest1.cs — 這是一個(gè)引用類(lèi),它只提供一個(gè)基單元測(cè)試(包括 TestClass、TestInitialize、TestCleanup 和 TestMethod 的定義)。
|
| ? |
BankAccountTest.cs — 這是特定于程序集生成的單元測(cè)試代碼。讓我們仔細(xì)看看該代碼,它是代碼生成過(guò)程中最重要的部分。
|
由 Unit Testing 引擎生成的類(lèi)包括以下組件:
| ? |
Using/imports 語(yǔ)句,用于引用的程序集。
|
| ? |
TestClass() 定義,用于包含該測(cè)試的類(lèi) (BankAccountTestFixture)。
|
| ? |
一個(gè)私有訪問(wèn)器和用于 TestContext 的公共屬性。它由單元測(cè)試運(yùn)行器(即 VSTS Unit Test Framework)使用,以便提供關(guān)于當(dāng)前測(cè)試運(yùn)行的信息以及用于該運(yùn)行的功能。
|
| ? |
TestInitialize() 和 TestCleanup() 方法。這些方法常用于獲取和釋放測(cè)試所需的任何對(duì)象。
|
| ? |
TestMethod(),用于每個(gè)選定的方法。
|
讓我們仔細(xì)看一下 DepositMoneyTest(),它負(fù)責(zé)確保當(dāng)前的平衡能反映原始數(shù)量與累計(jì)數(shù)量的總和。
///
///A test case for DepositMoney (float)
///
[TestMethod()]
public void DepositMoneyTest()
{
float initialBalance = 0; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance);
float depositAmt = 0; // TODO: Initialize to an appropriate value
target.DepositMoney(depositAmt);
Assert.Inconclusive("A method that does not return a value" +
"cannot be verified.");
}
請(qǐng)注意該生成引擎除創(chuàng)建一個(gè) stub TestMethod() 對(duì)象外,是如何進(jìn)行其他操作的。它創(chuàng)建了適用于接口的示例單元測(cè)試,包括:
| ? |
BankAccount 對(duì)象的分配和結(jié)構(gòu)(測(cè)試的對(duì)象主題)
|
| ? |
本地變量的創(chuàng)建和默認(rèn)分配,這些變量表示作為該測(cè)試主題的方法/構(gòu)造函數(shù)所需的參數(shù)。
| ? |
TODO 注釋?zhuān)嵝验_(kāi)發(fā)人員適當(dāng)?shù)胤峙鋮?shù)變量。
|
|
| ? |
如果測(cè)試基于一個(gè)源對(duì)象方法調(diào)用,則生成的代碼將包含對(duì)該方法(帶有用于這些參數(shù)的局部變量)的調(diào)用。
|
| ? |
初始 Assert() 方法調(diào)用,基于該方法的返回值。
|
| ? |
Assert.Inconclusive() 方法調(diào)用,作為完成測(cè)試代碼的提示程序。非確定性測(cè)試將在 Test Results 對(duì)話框中顯示為失敗。
|
生成后:我現(xiàn)在需要做什么?
考慮要完成相同的操作可以不必做哪些事情,則通常可以認(rèn)識(shí)到代碼生成的好處。在我們的示例中,我們不必:
| ? |
創(chuàng)建單元測(cè)試項(xiàng)目。
|
| ? |
設(shè)置項(xiàng)目引用。
|
| ? |
添加適當(dāng)?shù)臏y(cè)試類(lèi)(一個(gè)或多個(gè))。
|
| ? |
生成主干 Unit Test Framework 類(lèi)和屬性。
|
| ? |
創(chuàng)建單個(gè)測(cè)試方法。
|
| ? |
創(chuàng)建特定于接口的邏輯。
|
由于代碼生成過(guò)程創(chuàng)建了特定于對(duì)象接口的示例單元測(cè)試,因此我們接近于初始測(cè)試的完成階段了。通常情況下,只需“填充空白”并完成斷言(一個(gè)或多個(gè)),方法是將“已知的數(shù)據(jù)值”分配給屬性變量并創(chuàng)建適當(dāng)?shù)?Assert() 方法。顯然,這不是針對(duì)所有測(cè)試的示例,特別是對(duì)具有多個(gè)斷言的復(fù)雜測(cè)試而言。
只需幾秒鐘的時(shí)間(使用相對(duì)較少的擊鍵),您就能夠?qū)⑸傻膯卧獪y(cè)試代碼轉(zhuǎn)換為這些實(shí)際的測(cè)試。
例如,請(qǐng)考慮我們以如下方式開(kāi)始。
[TestMethod()]
public void DepositMoneyTest()
{
float initialBalance = 0; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance);
float depositAmt = 0; // TODO: Initialize to an appropriate value
target.DepositMoney(depositAmt);
Assert.Inconclusive("A method that does not return a value " +
"cannot be verified.");
}
我們能夠完成相對(duì)容易且具有有限擊鍵的測(cè)試(更改部分用黑體表示)。
[TestMethod()]
public void DepositMoneyTest() {
float currentBalance = 500;
BankAccount target = new BankAccount(currentBalance);
float depositAmt = 10;
target.DepositMoney(depositAmt);
Assert.AreEqual(currentBalance + depositAmt, target.CurrentBalance,
"Deposit Test: Deposit not applied correctly");
}
重新生成單元測(cè)試代碼
好消息是,代碼生成過(guò)程不會(huì)讓您重寫(xiě)以前生成(和修改)的單元測(cè)試。使用 Visual Studio 2005 Team System 的 Beta 2 版本,代碼生成選項(xiàng)提供一個(gè)啟用/禁用創(chuàng)建已存在測(cè)試的復(fù)選框。如果選擇它,而且該過(guò)程找到了一個(gè)具有相同名稱(chēng)的現(xiàn)有測(cè)試,則該過(guò)程將忽略該測(cè)試方法,并創(chuàng)建后續(xù)測(cè)試,從而將一個(gè)數(shù)字附加到該方法名的末尾。這通常在對(duì)象中使用重載的方法或構(gòu)造函數(shù)時(shí)發(fā)生,或者當(dāng)單擊 Generate 按鈕而不取消選定現(xiàn)有測(cè)試時(shí)發(fā)生。
自動(dòng)化單元測(cè)試建議
雖然本節(jié)可以獨(dú)立成文,但這里只是一些您在創(chuàng)建單元測(cè)試時(shí)可以采納的基本建議。
| ? |
設(shè)計(jì)彼此獨(dú)立的單元測(cè)試,其中它們可以獨(dú)立運(yùn)行(由于可以通過(guò)測(cè)試 UI 隨意選擇或取消選定它們)。
|
| ? |
不要只進(jìn)行正面測(cè)試。請(qǐng)確保代碼能夠響應(yīng)任何方案,包括發(fā)生意外時(shí)(資源不可用,數(shù)據(jù)庫(kù)只讀等)。
|
| ? |
把自己當(dāng)作一個(gè) QA 人員,想象成一個(gè)測(cè)試人員,而不僅僅是一個(gè)開(kāi)發(fā)人員。您花在設(shè)計(jì)單元測(cè)試上的時(shí)間將有助于減少日后解決故障所用的時(shí)間。請(qǐng)注意對(duì)象的幾個(gè)小細(xì)節(jié):數(shù)據(jù)如何在對(duì)象之間傳輸?誰(shuí)使用它們?銷(xiāo)毀對(duì)象容易嗎?如果我“進(jìn)行此操作”,將會(huì)發(fā)生什么?
|
| ? |
跳出您自己的思維模式。盡可能多地對(duì)測(cè)試進(jìn)行頭腦風(fēng)暴。當(dāng)您完成時(shí),回頭查看您可能漏掉的內(nèi)容。來(lái)自團(tuán)隊(duì)成員的請(qǐng)求反饋 — 例如,他們創(chuàng)建了什么其他類(lèi)型的測(cè)試?其他人可能提供一個(gè)對(duì)熟悉自己代碼的開(kāi)發(fā)人員而言非常困難的觀點(diǎn)。
|
| ? |
代碼覆蓋。使用 VSTS 代碼覆蓋規(guī)范提供有關(guān)每個(gè)測(cè)試運(yùn)行中實(shí)際執(zhí)行多少代碼的信息(代碼的行數(shù),占所有代碼的百分比)。如果編碼完成,并且通過(guò)了所有測(cè)試,但代碼覆蓋顯示只執(zhí)行了該邏輯的一小部分,那么您的測(cè)試真的成功了嗎?高代碼覆蓋不一定意味著您具有一個(gè)完整的“測(cè)試”集,而未覆蓋的代碼通常非常適用于一個(gè)新的測(cè)試用例。
|
| ? |
當(dāng)生成單元測(cè)試時(shí),要幫助其他人了解您的代碼:
| ? |
使用一個(gè)項(xiàng)目結(jié)構(gòu),該結(jié)構(gòu)映射所測(cè)試程序集的結(jié)構(gòu)。
每個(gè)程序集有一個(gè)相關(guān)的測(cè)試程序集。
每個(gè)類(lèi)有一個(gè)相關(guān)的測(cè)試類(lèi)。
在各自的測(cè)試方法中包含每個(gè)方法名(即,Load() 將有 PositiveLoadTest()、NegativeLoadTest()、PositiveScalarLoadTest() 等的測(cè)試方法)。
|
| ? |
使用一致的命名協(xié)定,包括對(duì)象的屬性和方法名。
|
|
| ? |
此外,當(dāng)其他所有測(cè)試都失敗時(shí),請(qǐng)進(jìn)行調(diào)試。自動(dòng)化單元測(cè)試應(yīng)該有助于減少您用在調(diào)試器上的時(shí)間。但是,如果測(cè)試結(jié)果和代碼覆蓋無(wú)法提供測(cè)試失敗的原因,那么您大可不必?fù)?dān)心調(diào)試單元測(cè)試。從 Beta 2 版的 Visual Studio 2005 Team System 開(kāi)始,開(kāi)發(fā)人員可以使用 Test Manager 中的 Debug checked tests 選項(xiàng)調(diào)試他們的單元測(cè)試程序集。
|
小結(jié)
自動(dòng)化單元測(cè)試為開(kāi)發(fā)環(huán)節(jié)提供了一個(gè)結(jié)構(gòu)化、自行紀(jì)錄、高度便攜且可重復(fù)的過(guò)程。如果在搜索現(xiàn)有程序集,或者如果開(kāi)發(fā)環(huán)境需要在開(kāi)始開(kāi)發(fā)之前進(jìn)行完整的設(shè)計(jì),則請(qǐng)考慮使用內(nèi)置到 Microsoft Visual Studio 2005 Team System 中的代碼生成引擎。Visual Studio 2005 Team System 的單元測(cè)試代碼生成功能可以為您節(jié)省寶貴的時(shí)間,而且有助于強(qiáng)制團(tuán)隊(duì)的開(kāi)發(fā)標(biāo)準(zhǔn)和約定。通過(guò)生成用于自動(dòng)化單元測(cè)試的基本內(nèi)容,包括生成帶有對(duì)象創(chuàng)建的測(cè)試方法、參數(shù)變量和基斷言類(lèi),您應(yīng)該能夠順利地在您的開(kāi)發(fā)方法論中采用自動(dòng)化單元測(cè)試。
作者簡(jiǎn)介
作為針對(duì) telligent 系統(tǒng)的專(zhuān)業(yè)服務(wù)主管,Scott Dockendorf 擅長(zhǎng)于用 .NET 提供高性能、可伸縮的應(yīng)用程序。Scott 熱衷于解決方案體系結(jié)構(gòu)、安全開(kāi)發(fā),他通過(guò)標(biāo)準(zhǔn)和已通過(guò)驗(yàn)證的方法論幫助企業(yè)采用推薦的實(shí)踐。Scott 是 .NET 社區(qū)的一個(gè)積極成員,他自愿作為 North Dallas .NET User Group 的程序主管。他還參加了面向本地 .NET 用戶(hù)組的會(huì)議,而且是 INETA's International Academic Committee for Texas 的活躍分子。您可以通過(guò)他的電子郵件 (scottd@telligent.com) 或網(wǎng)絡(luò)日記 (http://weblogs.asp.net/scottdockendorf) 與他聯(lián)系。
轉(zhuǎn)到原英文頁(yè)面