簡介
隨著業務的革新和發展,運行它們的系統也需要進行更新。隨業務的發展、革新以及與合作伙伴、客戶和供應商的結合,這些系統將在復雜性方面持續擴增。 這種復雜性迫使 IT 的領導者們在開發過程中(即,在實現之前)確保質量。有一種方法可使開發人員減少進入 QA 環節的故障數量,即,針對自定義代碼嚴格執行自動化單元測試。在開發過程中強制使用自動化單元測試可為團隊成員提供有關如何使用自定義代碼的示例(這些示例易于使用并自行記錄)。 使用結構化、自動化單元測試面臨的挑戰之一是完成這些任務所需的代碼總數。(測試代碼需要使用大量代碼?。┐a生成的概念(簡單定義為“創建軟件的軟件”)正隨著時間的快速推移而逐漸深入到團隊 IT 開發之中。有些人認為代碼生成有助于縮短“推向市場”策略的時間,強制內部標準/協定,并促進開發過程。 Microsoft 認識到這一需要后提供了一個功能豐富、帶有下一代開發平臺 Visual Studio 2005 Team System (VSTS) 的代碼生成引擎。本文提供針對單元測試代碼生成的循序漸進的指導,并深入探討如何在用例中使用。 vsts/UTFwVS05TmSys.mspx#top">
重新思考單元測試請考慮以下情況:您負責為公司生成下一代系統,同時您是較大的開發團隊中的一員。您是 UI 開發人員,負責盡可能多地創建 Microsoft ASP.NET/Microsoft WinForms。您依賴“中間層”團隊完成其中間層組件 — 這些組件用于執行數據庫 CRUD (Create-Retrieve-Update-Delete) 以及與該系統中每個實體相關的業務規則。 經過幾周的 UI 開發,您完成了窗體并且收到了中間層開發人員打算向您提交其類庫的消息。下面提供一段對話示例,說明我們大多數人在開發過程中都會遇到的一些事情。
在考慮如何進行這樣有趣的 項目之后,您打定了主意,決定檢驗中間層的單元測試套件。在深入鉆研該代碼之后,您注意到該窗體有兩個未標記的文本框,以及三個標記為 button1、button2 和 button3 的按鈕(幸運的話,它們將排列在窗體上)。接下來,在查看與這些按鈕相關的事件之后,您認識到這些代碼都未經注釋,并且數據變量都被命名為 x、y、z。如果幸運,您還會注意到 button1 和 button2 執行該對象的 Save() 方法,而 button3 執行 Delete() 方法。執行時,您會接收到很多 System.Exception 錯誤,這是因為遺漏了很多配置設置。 這顯然是一個特例,我希望多數開發團隊不要進行這一試驗,下面讓我們看一下該方案中“單元測試”遇到的問題:
輸入自動化單元測試xUnit 框架在 1998 年作為 eXtreme 編程的核心概念引入。它提出了一個有效的機制,有助于開發人員將結構化、有效且自動的單元測試添加常規開發活動中。從那以后,該框架演化為針對自動化單元測試框架的實際標準。 創建自動化單元測試的用例簡單說,自動化單元測試是:
xUnit 框架元素表 2 分析 xUnit 框架以及對應于 Visual Studio 2005 Team System 的 Unit Testing Framework 等價物的基本概念。
測試裝置示例請考慮以下針對 BankAccount 類的類關系圖,以及一個示例測試裝置 (BankAccountTests.cs)。 ![]() 圖 1. BankAccount 類 示例測試裝置: BankAccountTests.csusing 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"); } } } 主單元測試概念 == 斷言用于該形式單元測試的主要概念是,自動化單元測試是基于“斷言”的,即可定義為“事實或您相信為事實的內容”。從邏輯角度看,請考慮該語句“when I do {x}, I expect {y} as a result”。 這可以輕松地翻譯為代碼,方法是使用 Microsoft.VisualStudio.QualityTools.UnitTesting.Framework 命名空間中可用的三個“斷言”類中的任一個:Assert、StringAssert 和 CollectionAssert。主類 Assert 提供用于測試基礎條件語句的斷言。StringAssert 類自定義了在使用字符串變量時有用的斷言。同樣,CollectionAssert 類包括在使用對象集合時有用的斷言方法。 表 3 顯示可用于當前版本 Unit Testing Framework 的斷言。
這些自動化單元測試用什么運行?正如前面提到的,xUnit 框架將“測試運行器”的概念定義為應用程序負責:(a) 執行單元測試;(b) 報告測試結果。對于本文,包含 Visual Studio 2005 Team System (VSTS) 的 Unit Testing 引擎作為我們的“測試運行器”。圖 2 表示 BankAccountTests.cs 類的執行結果。 ![]() 圖 2. 測試結果窗格:單元測試執行結果 Microsoft Visual Studio 2005 使用源項目的代碼模型動態填充該視圖。它基于該源代碼中的自定義屬性動態發現有關該測試套件的信息。表 4 表示最常見的單元測試屬性(以及執行的次序)。
我編寫什么類型的測試?一個方法及其相關測試之間很難有一對一關系。編寫自動化單元測試需要開發人員“進行全面思考”,并了解關于對象的所有內容 — 它將如何消耗、使用、處理,以及在任何情況下如何起到積極、消極、非決定性作用。 例如,請考慮一個用于針對數據庫中 Customer 項執行 CRUD(創建、檢索、更新、刪除)功能的典型對象方法。對于該對象的 Load() 方法,要針對以下方案編寫測試:
這些只是自動化單元測試套件許多用法中的一部分。我曾經聽說一個小團隊使用單元測試查看針對其組件的已知安全攻擊。從宏觀的角度來看,單元測試應該明確保證組件的正常使用。具有豐富的測試集將使團隊確信您已經準確實現了既定的目標:編寫有效的軟件。無論自信源自哪里 — 這就是您需要編寫的測試。 您測試什么?從本質上看,這些自動化單元測試非常低級。它們旨在測試下至構造函數、方法調用的對象,甚至是對象上的屬性。 關于“公共對私有”的主題在單元測試派系中引發了許多爭論。許多人認為單元測試只應該測試對象的公共接口。其他人認為應該測試每個調用 — 包括內部私有方法。VSTS 支持兩個單元測試級別。VSTS 通過使用私有訪問器或包裝類支持私有測試,后者提供基于“私有”方法和屬性生成單元測試的功能。 為什么生成代碼?閱讀上面的列表后,您可能會想起前面項目的單個對象,并思考:“如果我用“這些”對象進行該操作,就需要編寫大量代碼!”請考慮開發人員仍編寫“單元測試”代碼的事實 — 只在不同的窗體(例如,前面提到的 WinForms 示例)上進行。此外,具有可自行記錄、可重用的實現示例帶來的好處遠大于生成更多代碼所帶來的麻煩。最后,在單元測試中設計更多的環節已證明可以減少質量保證環節中的故障。 正如前面所提到的,代碼生成是“軟件創建軟件”的過程?;诳芍貜偷倪^程創建代碼是理想的。例如,一些使用代碼生成的較好示例包括:腳本數據、創建表示實體及其在儲存庫(數據庫 CRUD)中存在的對象,或者創建適用于數據維護的 UI 控件。使用代碼生成的好處包括:
正如您所預期的,自動化單元測試屬于“優秀代碼生成候選者”。在現有組件中指出一些內容并針對這些自動單元測試生成初始代碼難道不是很理想嗎?而且不只是保留單元測試框架,還會圍繞對象的公共接口生成實現示例嗎?將來的 Visual Studio 2005 Team System 用戶將擁有該功能以及更多功能! 讓我們生成一些代碼吧!本例中,我們將生成本文前面提到的 BankAccount 類的代碼。本文的這一部分旨在為您介紹代碼生成過程,并重點介紹所提供的功能以及從 VSTS 使用 Unit Testing 引擎的好處。 第 1 步:創建您的實現代碼首先,我們創建一個將用作應用程序的業務層的類庫項目。 要在 VS 2005 中創建該庫:
VS 2005 創建該類后,下一個任務就是創建針對您的項目設計的 BankAccount 類。為此,需要執行以下操作:
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 引擎內置于 Visual Studio 2005 Team System,因此生成代碼比以前更容易。除了生成單元測試結構之外,它將生成特定于實例的信息,例如,對象創建、類型化參數和方法執行。 VS 2005 提供在任何類結構級別生成單元測試代碼的功能,這些級別包括命名空間、類、方法、屬性、構造函數,等等??赏ㄟ^右鍵單擊這些代碼元素并單擊 Generate test(s)(圖 3)進行此操作。 ![]() 圖 3. Generate test(s) 方法 因此,要開始代碼生成過程,請執行以下步驟:
現在,應該為您提供 Generate Unit Tests 對話框(如圖 4 所示)。該對話框及其組件提供對該過程中生成的代碼進行自定義的功能。讓我們看一下所有這些元素。 ![]() 圖 4. 生成 Unit Tests 對話框 Current selection: 樹視圖允許導航自定義類及其元素。VS 2005 使用反射填充該樹視圖,并在右鍵單擊以及單擊 Create Tests 的位置自動選擇組件。圖 3 中,由于我在類級別進行了此操作,因此該對話框自動選擇用于代碼生成的所有類元素。如果選擇在單個級別(即,構造函數、屬性或方法)進行生成,則只選擇那些元素。 Filter 選項(位于右上角)提供修改樹視圖(圖 5)中所示結果的功能,包括顯示非公共項、基類型以及“只屬于我的代碼”。如果使用的是大型解決方案,或者感覺顯示私有的內部結構會弄亂選擇窗口,那么這對您很有益處。 ![]() 圖 5. 篩選選擇結果 下一個是 Output project 對話框(位于 Current selection: 樹視圖下)。該列表框允許您針對生成的測試裝置選擇目的項目(圖 6)。如果您的解決方案包含以前創建的測試項目,則將包含該測試項目以供選擇。由于這是我們首次訪問該對話框,因此可以選擇 Create a new … Test Project 選項。 ![]() 圖 6. 輸出項目選擇 要繼續我們的過程,請執行以下操作:
最后,該對話框提供通過 Settings 按鈕(位于左下角)自定義代碼生成過程的功能。單擊該按鈕將加載 Test Generation Settings 對話框,如圖 7 所示。 ![]() 圖 7. Test Generation Settings 對話框 該對話框允許您進行以下更改:
要完成我們的配置并生成單元測試代碼(以及更多),請執行以下操作:
VS 2005 將顯示一個進度欄,提供代碼生成過程中的狀態。該過程將在幾秒鐘內完成,您可以看到一個名為 BankAccountTest.cs 的類。 生成了什么?在我們對該測試裝置進行特別查看之前,讓我們看一下在代碼生成過程中創建了什么。 首先,它創建了 Test Class Library 項目 BankAccountDemo.Business.Test。請注意該項目如何包含對實現類 BankAccountDemo.Business(您從其中生成代碼)和 Microsoft.VisualStudio.QualityTools.UnitTestFramework 類庫的引用。在查看該類的內容時,您將注意到以下文件:
由 Unit Testing 引擎生成的類包括以下組件:
讓我們仔細看一下 DepositMoneyTest(),它負責確保當前的平衡能反映原始數量與累計數量的總和。 /// ///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."); } 請注意該生成引擎除創建一個 stub TestMethod() 對象外,是如何進行其他操作的。它創建了適用于接口的示例單元測試,包括:
生成后:我現在需要做什么?考慮要完成相同的操作可以不必做哪些事情,則通??梢哉J識到代碼生成的好處。在我們的示例中,我們不必:
由于代碼生成過程創建了特定于對象接口的示例單元測試,因此我們接近于初始測試的完成階段了。通常情況下,只需“填充空白”并完成斷言(一個或多個),方法是將“已知的數據值”分配給屬性變量并創建適當的 Assert() 方法。顯然,這不是針對所有測試的示例,特別是對具有多個斷言的復雜測試而言。 只需幾秒鐘的時間(使用相對較少的擊鍵),您就能夠將生成的單元測試代碼轉換為這些實際的測試。 例如,請考慮我們以如下方式開始。 [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 版本,代碼生成選項提供一個啟用/禁用創建已存在測試的復選框。如果選擇它,而且該過程找到了一個具有相同名稱的現有測試,則該過程將忽略該測試方法,并創建后續測試,從而將一個數字附加到該方法名的末尾。這通常在對象中使用重載的方法或構造函數時發生,或者當單擊 Generate 按鈕而不取消選定現有測試時發生。 自動化單元測試建議雖然本節可以獨立成文,但這里只是一些您在創建單元測試時可以采納的基本建議。
小結自動化單元測試為開發環節提供了一個結構化、自行紀錄、高度便攜且可重復的過程。如果在搜索現有程序集,或者如果開發環境需要在開始開發之前進行完整的設計,則請考慮使用內置到 Microsoft Visual Studio 2005 Team System 中的代碼生成引擎。Visual Studio 2005 Team System 的單元測試代碼生成功能可以為您節省寶貴的時間,而且有助于強制團隊的開發標準和約定。通過生成用于自動化單元測試的基本內容,包括生成帶有對象創建的測試方法、參數變量和基斷言類,您應該能夠順利地在您的開發方法論中采用自動化單元測試。 |
原文轉自:http://www.anti-gravitydesign.com