原著:NUnit v2.1 原文:NUnit文檔之QuickStart.doc 翻譯:lover_P -------------------------------------------------------------------------------- [譯者序] 縱觀軟件的開發,javascript:;" onClick="javascript:tagshow(event, '%B2%E2%CA%D4');" target="_self">測試已經日益成為軟件開發過程中的重要環節,通常一個軟件的開發周期中測試要占到一半時間甚至更多。而在測試過程中,單元測試更是萬里長征第一步,單元測試進行得是否完善,直接影響到后期集成測試的效率。進行單元測試,有許多軟件可以自動完成,NUnit就是其中之一。這是一款與JUnit齊名的,同屬于xUnit家族的單元測試軟件(在http://www.NUnit.org我們可以免費得到這款軟件)。 [正文] 讓我們從一個簡單的例子開始。假設我們正在編寫一個銀行應用程序,而我們有一個這一領域的基本類——Aclearcase/" target="_blank" >ccount。Account支持存款、取款和資金轉帳。這個Account類看起來會是這個樣子: namespace bank { public class Account { private float balance; public void Deposit(float amount) { balance += amount; } public void Withdraw(float amount) { balance -= amount; } public void TransferFunds(Account destination, float amount) { } public float Balance { get { return balance; } } } } 現在我們來為這個類寫一個測試——AccountTest。我們要測試的第一個類方法是TransferFunds。 namespace bank { using NUnit.Framework; [TestFixture] public class AccountTest { [Test] public void TransferFunds() { Account source = new Account(); source.Deposit(200.00F); Account destination = new Account(); destination.Deposit(150.00F); source.TransferFunds(destination, 100.00F); Assert.AreEqual(250.00F, destination.Balance); Assert.AreEqual(100.00F, source.Balance); } } } 首先要注意的是這個類關聯了一個[TestFixture]特性(attribute)——這表示這個類包含了測試代碼(這個特性可以被繼承)。這個類必須是公有的,但他的父類并不受限制。這個類還必須有一個默認構造函數。 類中唯一的一個方法——TransferFunds(),關聯了一個[Test]特性——這表示它是一個測試方法。測試方法的返回值必須為void并且不能帶有參數。在我們的測試方法中,我們對被測試的對象進行了一般的初始化,執行了被測試的方法并檢查了對象的狀態。Assert類定義了一組方法用于檢查給定的條件,在我們的例子中我們使用了AreEqual()方法來確保交易過后兩個賬戶都有正確的余額(這個方法有很多重載,我們在這個例子中使用的版本帶有兩個參數:第一個參數是我們的期望值,第二個參數是實際值)。 編譯并運行這個例子。假設你已經將你的測試代碼編譯為bank.dll。打開NUint Gui(安裝程序會在你的桌面和“程序”菜單中建立一個快捷方式),打開GUI后,選擇File->Open菜單項,找到你的bank.dll并在“Open”對話框中選中它。bank.dll裝載后你會在左邊的面板中看到一個測試樹結構,還有右邊的一組狀態面板。單擊Run按鈕,狀態條和測試樹種的TransferFunds節點變成了紅色——我們的測試失敗了?!癊rrors and Failures”面板顯示如下消息——“TransferFunds: expected <250> but was <150>”,在它正下方的堆棧跟蹤面板報告了測試失敗的語句在代碼中的位置——“at bank.AccountTest.TransferFunds() in C:\nunit\BankSampleTests\AccountTest.cs:line 17” 這正是預期的結果,因為我們還未實現TransferFunds()方法?,F在我們來搞定它。不要關閉GUI,回到你的IDE并修改代碼,使你的TransferFunds()方法看起來像這樣: public void TransferFunds(Account destination, float amount) { destination.Deposit(amount); Withdraw(amount); } 現在重新編譯你的代碼并再次在GUI中點擊Run按鈕——狀態條和數節點變綠了。(注意GUI會自動地為你重新加載程序集;我們可以一直開著GUI而在IDE中繼續工作并寫更多的測試)。 讓我們來為我們的Account的代碼添加一些錯誤檢測。為賬戶添加一個最小余額限制,通過你的最小透支保護費來維持它的持續運作。首先我們來為Account類添加一個最小余額保護屬性: private float minimumBalance = 10.00F; public float MinimumBalance { get { return minimumBalance; } } 我們使用一個異常來指出透支: namespace bank { using System; public class InsufficientFundsException : ApplicationException { } } 向我們的AccountTest類添加一個新的方法: [Test] [ExpectedException(typeof(InsufficientFundsException))] public void TransferWithInsufficientFunds() { Account source = new Account(); source.Deposit(200.00F); Account destination = new Account(); destination.Deposit(150.00F); source.TransferFunds(destination, 300.00F); } 這個測試方法除了[Test]特性之外還關聯了一個[ExpectedException]特性——這指出測試代碼希望拋出一個指定類型的異常;如果在執行過程中沒有拋出這樣的一個異?!摐y試將會失敗。編譯你的代碼并回到GUI。由于你編譯了你的測試代碼,GUI會變灰并重構了測試樹,好像這個測試還沒有被運行過(GUI可以監視測試程序集的變化,并在測試樹結構發生變化時進行更新——例如,添加了新的測試)。點擊“Run”按鈕——我們又一次得到了一個紅色的狀態條。我們得到了下面的失敗消息:“TransferWithInsufficentFunds: InsufficientFundsException was expected”。我們來再次修改Account的代碼,象下面這樣修改TransferFunds()方法: public void TransferFunds(Account destination, float amount) { destination.Deposit(amount); if(balance - amount < minimumBalance) throw new InsufficientFundsException(); Withdraw(amount); } 編譯并運行測試——綠了。成功!不過等等,看看我們剛寫的代碼,我們會發現銀行在每一筆不成功的轉賬操作時都虧錢了。讓我們來寫一個測試來確認我們的猜測。添加這個測試方法: [Test] public void TransferWithInsufficientFundsAtomicity() { Account source = new Account(); source.Deposit(200.00F); Account destination = new Account(); destination.Deposit(150.00F); try { source.TransferFunds(destination, 300.00F); } catch(InsufficientFundsException expected) { } Assert.AreEqual(200.00F,source.Balance); Assert.AreEqual(150.00F,destination.Balance); } 我們測試了方法的交易屬性——是否所有的操作都成功了。編譯并運行——紅條。是的,我們平白無故地損失了300塊錢——source賬戶有正確的余額150.00,但destination賬戶顯示:$450.00。我們該如何修改?我們能夠只將最小余額檢查的調用放到數據更新的前面么: public void TransferFunds(Account destination, float amount) { if(balance - amount < minimumBalance) { throw new InsufficientFundsException(); } destination.Deposit(amount); Withdraw(amount); } 如果Withdraw()方法拋出了另外一個異常呢?我們應該在catch塊中執行一個補救處理,還是依賴我們的交易管理器來重新裝載對象的狀態?某些時候我們必須回答這樣的問題,但不是現在;可我們眼前如何應付這個失敗的測試呢——刪除它?一個不錯的方法是臨時忽略它在你的測試方法中添加下面的特性: [Test] [Ignore("Need to decide how to implement transaction management in the application")] public void TransferWithInsufficientFundsAtomicity() { // code is the same } 編譯并運行——黃條。單擊“Test Not Run”選項卡,你會看到bank.AccountTest.TransferWithInsufficientFundsAtomicity()連同這個測試被忽略的原因一起列在列表中。 看看我們的測試代碼,我們可以看到一些適宜的重構。所有的方法共享一組公共的測試對象。讓我們來將這些初始化代碼放到一個setup方法中并在所有的測試中重用它們。我們的測試類的重構版本像下面這樣: namespace bank { using System; using NUnit.Framework; [TestFixture] public class AccountTest { Account source; Account destination; [SetUp] public void Init() { source = new Account(); source.Deposit(200.00F); destination = new Account(); destination.Deposit(150.00F); } [Test] public void TransferFunds() { source.TransferFunds(destination, 100.00f); Assert.AreEqual(250.00F, destination.Balance); Assert.AreEqual(100.00F, source.Balance); } [Test] [ExpectedException(typeof(InsufficientFundsException))] public void TransferWithInsufficientFunds() { source.TransferFunds(destination, 300.00F); } [Test, Ignore ( "Need to decide how to implement transaction management in the application" )] public void TransferWithInsufficientFundsAtomicity() { try { source.TransferFunds(destination, 300.00F); } catch(InsufficientFundsException expected) { } Assert.AreEqual(200.00F,source.Balance); Assert.AreEqual(150.00F,destination.Balance); } } } 注意這個初始化方法擁有通用的初始化代碼,它的返回值類型為void,沒有參數,并且由[SetUp]特性標記。編譯并運行——同樣的黃條! ============全文完========== 如果你安裝了NUnit V2.1,可以在 開始->程序->NUnit V2.1->QuickStart 處得到原文(英文版)。 |
原文轉自:http://www.anti-gravitydesign.com