1. 介紹
NUnit是 xUnit(JUnit,JsUnit,CPPUnit,DUnit)家族的一員,是專門針對.NET開發的測試架構,支援C#、VB.NET、 VC.NET的測試,另外還有支援測試ASP.NET的NunitAsp和測試Windows表單的NunitForms,我們這裡只討論Nunit,Nunit完全由C#語言來編寫,並且編寫時充分利用了許多.NET的特性,比如反射,客戶屬性等等,最重要的一點是它適合於所有.NET語言,它繼承了被廣泛使用的JUnit的優點與特性,可以很容易的上手或者進行自訂的擴充,NUnit在使用上非常簡單,甚至於微軟在企業庫的源碼中也提供了使用NUnit測試的版本。要擷取安裝包可以從www.nunit.org下載最新的安裝包即可。個人覺得它最讓人不爽的就是沒有提供IDE外掛程式,需要自己找一個喜歡的IDE外掛程式來使用,這樣可以更方便的進行調試之類的操作也省去了頻繁切換視窗的麻煩,推薦TestDriven.Net(這又針對個人的免費版本)或者Resharper(這個是要錢的,鄙視)。
TestDriven.NET(目前只支援到Nunit 2.4版本,對2.5的一些新功能還不支援):
TestDriven.Net現在是一種商業化的工具,只有個人版可以免費下載使用,個人認為在開發中個人版的功能已經足夠了,它的功能十分強大,它的前身是NUnitAddIn,由Jamie Cansdale開發,現在TestDriven.NET 2.0版本也已經發布,最新的2.0版本整合了.NET Reflector、NConver、NConverExplorer、TypeMock.NET等,功能更加強大。它使用很簡單,大家可以參考官方文檔
Resharper :
是一個功能豐富的IDE外掛程式,支援從代碼整理到程式碼分析等各種功能,對Nunit的支援是它的功能之一,個人認為是一個相當不錯的工具,開發過程可以將好 多工作自動完成,如果是一個菜鳥的話作用就更大了,可以協助改善個人的編碼習慣,使你的代碼看起來要好很多,先最新版本是4,感興趣的可以去找一下,網上 的破解版本也挺多的(雖然鄙視盜版,但以經濟建設為中心也沒辦法啊)。
選擇Nunit的理由:
1.開源工具(免費的),專註於測試架構的開發,功能豐富上手簡單,使用方便。
2.提供了比.NET架構更加豐富和易用的斷言模型。
3.運行方便、配置靈活,可以方便的對包含測試方法的dll進行測試,通過樹形菜單支援對多個程式集進行測試,並支援對測試方法進行分組管理。
4.對測試結果可以選擇性的儲存,省去了手動清理垃圾檔案的時間。
5.測試方法與實際代碼是緊密結合的,便於在更改代碼的時候同時維護測試案例,而且方便在脫離代碼和開發環境下進行測試,比如新部署之後可以直接執行一般測試尋找問題而不必用代碼去跟可能出現的問題。而VS.NET的測試方案無法脫離VS.NET開發環境獨立存在。
2.使用說明(官方文檔 )
從www.nunit.org 下載最新的安裝包安裝即可。如果要在工程總添加基於Nunit的單元測試,執行如下步驟:首先在工程引用中添加Nunit.Framework的引用,該 引用位於.NET選項卡,假如我們要測試的類名為Account.cs,則約定我們的測試類別名為AccountTest.cs,然後再該測試類別中添加測試 代碼。Nunit通過特性(attribute)來標記一個測試類別或者測試方法,其基本形式如下:
[TestFixture]
public class AccountTest //標識一個測試類別
//標識一個函數是測試方法入口
[Test]
public void TransferFunds()
[SetUp]//執行測試前的一些初始化操作
public void Init()
{
source = new Account();
source.Deposit(200.00F);
destination = new Account();
destination.Deposit(150.00F);
}
[TearDown]
public void Dispose() //執行測試完成後的資源清理操作
{
}
下面我們通過一個網上比較流行的例子來瞭解一下Nunit的具體使用方法(感謝整理這個例子的大俠):
假設我們正在編寫一個銀行應用程式,而我們這樣一個基本類——Account。Account支援存款、取款和資金轉帳。這個Account類看起來會是這個樣子:
namespace UnitTest
{
//賬戶類
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;
}
}
}//End Class
}//End Namespace
現在我們來為這個類寫一個測試——AccountTest。我們要測試的第一個類方法是TransferFunds。
using NUnit.Framework;
using System;
namespace UnitTest
{
[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);
}
}//End Class
}//End Namespace
首先這個類關聯了一個[TestFixture]特性(attribute)——這表示這個類包含了測試代碼(這個特性可以被繼承)。這個類必須是公有的,但他的父類並不受限制。這個類還必須有一個預設建構函式。
類中唯一的一個方法——TransferFunds(),關聯了一個[Test]特性——這表示它是一個測試方法。測試方法的傳回值必須為void並 且不能帶有參數。在我們的測試方法中,我們對被測試的對象進行了一般的初始化,執行了被測試的方法並檢查了對象的狀態。Assert類定義了一組方法用於 檢查執行結果是否符合預期設計,在例子中我們使用了AreEqual()方法來確保交易過後兩個賬戶都有正確的餘額(這個方法有很多重載(更多的介紹見後 面的附錄),我們在這個例子中使用的版本帶有兩個參數:第一個參數是我們的期望值,第二個參數是實際值)。
編譯並運行這個例子。假設你已經將你的測試代碼編譯為bank.dll。開啟NUint Gui(案頭或“程式”菜單中的一個捷徑),開啟GUI後,選擇File->Open功能表項目,找到你的bank.dll並在 “Open”對話方塊中選中它。bank.dll裝載後你會在左邊的面板中看到一個測試樹結構,還有右邊的一組狀態面板。單擊Run按鈕可以執行選定的測 試。在這裡我使用的是Reshaper提供的一個整合到IDE內的外掛程式來執行的,執行狀態如所示,狀態條和測試樹種的TransferFunds節點 變成了紅色——我們的測試失敗了。
這正是預期的結果,因為我們還未實現TransferFunds()方法。 修改代碼,使你的TransferFunds()方法看起來像這樣:
//轉賬
public void TransferFunds(Account destination, float amount)
{
destination.Deposit(amount);
Withdraw(amount);
}
再次測試——狀態條和數節點變綠了。
讓我們來為Account的代碼添加一些錯誤偵測。為賬戶添加一個最小餘額限制,通過最小透支保護費來維持它的運作。首先我們來為Account類添加一個最小餘額保護屬性:
public float minimumBalance = 10.00F;
/// <summary>
/// 最小餘額限制
/// </summary>
public float MinimumBalance
{
get
{
return minimumBalance;
}
}
我們使用一個異常來指出透支:
namespace UnitTest
{
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]特性——這指出測試代碼希望拋出一個指定類型的異常;如果在執 行過程中沒有拋出這樣的一個異常——該測試將會失敗。在測試類別中點擊右鍵選擇Run Unit Tests,結果如所示:
我們來再次修改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()方法拋出了另外一個異常呢?一個不錯的方法是臨時忽略它在你的測試方法中添加下面的特性:
[Test]
[Ignore("Need to decide how to implement transaction management in the application")]
public void TransferWithInsufficientFundsAtomicity()
{
// code is the same
}
測試結果——黃。
我們可以看到一些適宜的重構。所有的方法共用一組公用的測試對象。讓我們來將這些初始化代碼放到一個setup方法中並在所有的測試中重用它們。我們的測試類別的重構版本像下面這樣:
using NUnit.Framework;
using System;
namespace UnitTest
{
[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);
}
}//End Class
}//End Namespace
注意這個初始化方法擁有通用的初始化代碼,它的傳回值類型為void,沒有參數,並且由[SetUp]特性標記。編譯並運行——同樣的黃條!
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/sinory/archive/2009/12/04/4939402.aspx