極限編程(XP)越來越進(jìn)入程序員的眼球,TDD(Test Drived Design)也越來越普及,UT(Unit Testing)是TDD的第一步,主要面向的是一線的開發(fā)人員,而不是項(xiàng)目經(jīng)理、系統(tǒng)設(shè)計(jì)與分析人員甚至是測(cè)試人員,當(dāng)然UT的一些方法也可以用于后續(xù)的測(cè)試,但從概念上來講那已經(jīng)不算UT了。UT是“開發(fā)者寫的一小段代碼,用于檢驗(yàn)被測(cè)代碼的一個(gè)很小的、很明確的功能是否正確”。xUnit系列是專門用戶UT的框架(工具),包含了對(duì)目標(biāo)代碼進(jìn)行UT的類庫。NUnit是xUnit系列成員之一,用于對(duì).NET Framework下的語言(實(shí)際上只要符合CTS規(guī)范的語言NUnit都支持)所寫的代碼進(jìn)行UT。
UT的主要目的是提高代碼的健壯性和提高代碼的生產(chǎn)效率,NUnit是以此為初衷進(jìn)行設(shè)計(jì)的,所以對(duì)于使用VS.NET IDE開發(fā)而言,添加一下nunit.framework.dll的引用可以直接在工程中進(jìn)行單元測(cè)試,引用細(xì)節(jié)由IDE本身來執(zhí)行,對(duì)程序員來講是透明的。如果沒有使用VS.NET等大型IDE的,比如使用編輯器+SDK+命令行的方式進(jìn)行編碼,或者干脆使用Emacs,這時(shí)很多事情要手動(dòng)進(jìn)行了,這一過程可能有些boring,這也是沒辦法的事情,因?yàn)榫庉嬈?命令行本來生產(chǎn)力不高,但確是學(xué)習(xí)的好方式(也是我喜歡的方式)。這里,如果被測(cè)試代碼和測(cè)試代碼寫在不用的源文件里,進(jìn)行一次UT典型步驟如下(以C#為例并假設(shè)兩個(gè)文件分別為target.cs和test.cs):
·編譯被測(cè)試代碼為DLL或者M(jìn)odule:
csc /t:library target.cs 或者 csc /t:module target.cs
·編譯測(cè)試文件為DLL,并添加對(duì)外部庫文件nunit.framework.dll和target.dll的引用:
csc /t:library /r:c:progra~1
unit2~1.2in
unit.framework.dll;target.dll test.cs
或者只添加對(duì)外部庫文件nunit.framework.dll的引用并添加target模塊:
csc /t:library /r:c:progra~1
unit2~1.2in
unit.framework.dll /addmodule:target.netmodule test.cs
·NUnit命令行:
nunit-console test.dll
或者運(yùn)行NUnit GUI打開test.dll,進(jìn)行測(cè)試,生成測(cè)試報(bào)告。
這里假設(shè)NUnit安裝在c:program files unit 2.2目錄下,并且在命令行中要使用8.3格式的文件目錄名,因?yàn)閏sc使用過空格來區(qū)分不同編譯參數(shù)的。并且,對(duì)nunit.framework.dll的引用是必須的,不然在測(cè)試代碼中,聲明 using NUnit.Framework 會(huì)報(bào)“error CS0246: The type or namespace name @#NUnit@# could not be found (are you missing a using directive or an assembly reference?)”的錯(cuò)誤。
上述過程有點(diǎn)繁瑣,尤其如果代碼比較多的時(shí)候,每次更改都要進(jìn)行單元測(cè)試時(shí)都要進(jìn)行這幾步。把這一過程寫成一個(gè)批處理,然后每次執(zhí)行這個(gè)bat文件,可以緩解一點(diǎn),只是文件不同時(shí)要改寫這個(gè)批處理。
雖然把不同的類(被測(cè)試類和測(cè)試類)寫在不用源文件里是程序開發(fā)的通行的方式,但對(duì)于個(gè)人學(xué)習(xí)而言把被測(cè)試類和測(cè)試類寫在一個(gè)文件里,再運(yùn)用一下編輯器(我用的Ultraedit)用戶命令行工具,可以很大程度上簡化上述的編譯環(huán)節(jié),下面以Ultraedit為例來說明一下:類設(shè)計(jì)完以后,把所有的被測(cè)試類和測(cè)試類都放在同一個(gè)文件里(為使代碼脈絡(luò)清晰,可以在不用類代碼之間加插入一個(gè)分頁符,這并不影響編譯),然后點(diǎn)擊菜單“高級(jí)-〉工具欄配置”在命令行里輸入:
csc /t:library /r:c:progra~1 unit2~1.2in unit.framework.dll %n%e
工作目錄里輸入:%p,再分別選中“保存活動(dòng)文件”“輸出到列表窗口”“捕捉輸出”;然后再以同樣的方式新建一個(gè)用戶工具,其他參數(shù)都一樣,只是命令行改成:
nunit-console %n.dll
這兩個(gè)工具可分別命名為:C#NUnit聯(lián)合編譯,NUnit命令行。這時(shí)高級(jí)菜單下已經(jīng)多了這兩個(gè)命令,并分別有了默認(rèn)的快捷鍵。ok,下面先后點(diǎn)擊這兩個(gè)命令,能完成簡單的單元測(cè)試了。下面是一段簡單的源程序:
using NUnit.Framework;
using System;
// Class to be tested.
public class Target
{
public int add(int i, int j)
{
return i+j;
}
}
// Class to perform testing.
[TestFixture]
public class Test
{
[Test]
public void TestAdd()
{
Target testObj = new Target();
Assert.AreEqual(1, testObj.add(0, 1));
Assert.AreEqual(10, testObj.add(2, 7));
Assert.AreEqual(10, testObj.add(2, 8));
Assert.AreEqual(20, testObj.add(18, 3));
}
}
先后點(diǎn)擊那兩個(gè)命令后,NUnit將檢測(cè)出第二斷言的失敗。
綜合上述簡單介紹了手動(dòng)進(jìn)行單元測(cè)試的方法,按照這種方式同樣可以添加nunit.core.dll的引用,來進(jìn)行一些復(fù)雜的單元測(cè)試。還是那句話,對(duì)于自己的學(xué)習(xí)而言,編輯器+SDK+命令行是很好的寫代碼方式,能幫助了解文件之間、配件(Assembly)之間等的關(guān)系,還有助于記憶類的體系結(jié)構(gòu)和準(zhǔn)確的方法屬性名,可畢竟不適于工業(yè)化生產(chǎn)。在用了一段時(shí)間IDE之后,回歸到樸素,更能體會(huì)到編程的樂趣。這讓我想起了那句話,怎么說來著:使用Emacs是程序員追求的一種精神