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