摘 要: 為了解決單元測試工具Nunit本身不支持?jǐn)?shù)據(jù)驅(qū)動(dòng)測試的問題,,提出了在Nunit框架下實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)測試的方法,。該方法首先將測試類所使用的測試數(shù)據(jù)基本信息設(shè)定在ini文件中,將輸入數(shù)據(jù)及預(yù)期結(jié)果存放于Excel文件中,。隨后通過屬性標(biāo)簽[TestFixtureSetUp]標(biāo)記的方法動(dòng)態(tài)讀取ini文件中的基本信息,,再根據(jù)這些基本信息讀取Excel文件中的測試數(shù)據(jù),并將測試數(shù)據(jù)保存于自定義的結(jié)構(gòu)體數(shù)組中供各測試方法使用,。該方法有效地實(shí)現(xiàn)了測試數(shù)據(jù)與測試腳本的分離,,能降低測試腳本的維護(hù)工作量,提高測試效率,。
關(guān)鍵詞: 單元測試; Nunit框架; 測試腳本; 測試數(shù)據(jù)
軟件測試是保證軟件質(zhì)量的重要手段,,作為軟件測試基礎(chǔ)的單元測試是軟件開發(fā)過程中最低級的測試活動(dòng),。據(jù)業(yè)界統(tǒng)計(jì),,單元測試一般可以發(fā)現(xiàn)大約80%的軟件缺陷[1]。由于軟件缺陷發(fā)現(xiàn)得越早,,其修復(fù)的代價(jià)就越小,,因此,,單元測試在軟件測試過程中占據(jù)著非常重要的地位,有效地實(shí)施單元測試能有效節(jié)約后續(xù)測試時(shí)間,、降低測試成本,、保證軟件質(zhì)量。
Nunit是微軟.Net平臺(tái)下的單元測試框架,,也是在.Net平臺(tái)下進(jìn)行測試驅(qū)動(dòng)開發(fā)的重要工具,;其提供的圖形用戶接口GUI操作簡單、內(nèi)容直觀,,因此Nunit被廣泛地運(yùn)用于.Net平臺(tái)項(xiàng)目的單元測試中,。利用Nunit進(jìn)行單元測試,首先要進(jìn)行腳本開發(fā),,目前常用的方法是將測試數(shù)據(jù)以常量的方式賦值給變量,,再由變量參與測試[2-3]。采用這種方式,,測試數(shù)據(jù)與測試腳本共存于一個(gè)測試腳本文件中,,不利于測試腳本的維護(hù)。而測試腳本的維護(hù)是自動(dòng)化測試的重要環(huán)節(jié),,適當(dāng)?shù)卣{(diào)整和增強(qiáng)測試腳本,,能提高測試腳本的靈活性以及應(yīng)對測試對象變更的能力,數(shù)據(jù)驅(qū)動(dòng)方式的測試腳本開發(fā)是解決這類問題的重要手段[4],。由于Nunit框架本身并不具備數(shù)據(jù)驅(qū)動(dòng)測試的功能,,因此需要通過增強(qiáng)測試腳本的功能來實(shí)現(xiàn)Nunit框架下的數(shù)據(jù)驅(qū)動(dòng)測試。為此,,本文提出一種測試設(shè)計(jì)方法,,將測試用例的輸入數(shù)據(jù)、預(yù)期結(jié)果及執(zhí)行條件編寫在Excel工作簿中,,工作簿中每一個(gè)工作表的數(shù)據(jù)對應(yīng)一個(gè)測試類,;工作簿存放的位置、測試類對應(yīng)的工作表名,、輸入數(shù)據(jù)數(shù)目及預(yù)期結(jié)果數(shù)目等信息按一定格式保存于執(zhí)行路徑下的ini文件中,。在測試腳本中,利用Nunit的[TestFixtureSetUp]屬性標(biāo)簽,,在該標(biāo)簽標(biāo)記的方法中實(shí)現(xiàn)測試類所有測試數(shù)據(jù)的一次性讀入,,并將其保存于自定義的結(jié)構(gòu)體數(shù)組中,供各測試方法使用,,在后續(xù)測試方法的腳本編寫中,,只需按結(jié)構(gòu)體數(shù)組下標(biāo)獲取測試數(shù)據(jù)及預(yù)期結(jié)果便能完成測試腳本的開發(fā);測試執(zhí)行時(shí)將按照Excel文件中設(shè)計(jì)的數(shù)據(jù)自動(dòng)執(zhí)行測試腳本,。這一測試設(shè)計(jì)方法能有效地分離測試腳本和測試數(shù)據(jù),,從而降低測試腳本的維護(hù)工作量,。
1 數(shù)據(jù)驅(qū)動(dòng)測試的設(shè)計(jì)思路
數(shù)據(jù)驅(qū)動(dòng)測試是一種數(shù)據(jù)被包含在輸入測試數(shù)據(jù)文件中,并且以數(shù)據(jù)來控制自動(dòng)化測試腳本執(zhí)行的流程和動(dòng)作的測試[5],。在Nunit框架中,[TestFixtureSetUp]和[SetUp]屬性標(biāo)簽標(biāo)記的方法分別在測試類和各測試方法執(zhí)行前被執(zhí)行,,[Test]屬性標(biāo)簽標(biāo)記的方法按其在腳本中的先后順序自動(dòng)執(zhí)行;此外,通過Nunit的GUI界面,,可以指定被執(zhí)行的方法,,因此Nunit自身已經(jīng)具備控制測試腳本執(zhí)行流程的功能。這里主要從用數(shù)據(jù)驅(qū)動(dòng)腳本的動(dòng)作出發(fā)來實(shí)現(xiàn)數(shù)據(jù)驅(qū)動(dòng)測試,。
數(shù)據(jù)驅(qū)動(dòng)測試使用存檔的測試數(shù)據(jù)來驅(qū)動(dòng)自動(dòng)化測試過程,,這些數(shù)據(jù)通常以簡單的文本文件或Excel文件形式存在[5]。鑒于Excel文件具有以表格形式呈現(xiàn),、結(jié)構(gòu)清晰直觀的特點(diǎn),本文采用Excel的工作表來存儲(chǔ)測試數(shù)據(jù),。同時(shí),為使測試腳本具有較高的靈活性,,將測試數(shù)據(jù)文件的存取路徑,、測試數(shù)據(jù)所在的工作表名等信息存放于ini文件中,以便在測試環(huán)境和數(shù)據(jù)發(fā)生改變時(shí),測試腳本可以保持不變,從而降低測試腳本的維護(hù)成本,。
1.1 Excel文件格式的設(shè)計(jì)
圖1為Excel文件結(jié)構(gòu)與測試類及測試方法之間的對應(yīng)關(guān)系圖,,圖中的數(shù)據(jù)是根據(jù)Nunit自帶的一個(gè)實(shí)例Moneybag類所做的部分測試數(shù)據(jù)。
圖1中,,工作簿Moneytest.xls中的工作表Moneytest用來存放測試類Moneytest中各測試方法所需的數(shù)據(jù),。由于各測試方法常常使用一些公共的輸入數(shù)據(jù)來進(jìn)行測試驗(yàn)證,因此將公共數(shù)據(jù)設(shè)定在第一行,,從第二行數(shù)據(jù)開始每一行數(shù)據(jù)對應(yīng)一個(gè)測試方法,,每一個(gè)單元格存放一個(gè)基本數(shù)據(jù)(字符串類型或數(shù)值)。結(jié)構(gòu)數(shù)據(jù)類型都可以看成是基本數(shù)據(jù)類型的組合,,一個(gè)結(jié)構(gòu)數(shù)據(jù)類型可分成多個(gè)單元格來存放,。由于每一個(gè)測試方法所需的輸入和預(yù)期值數(shù)目各不相同,在測試數(shù)據(jù)設(shè)計(jì)時(shí),,可按需要增減工作表列的數(shù)目來增減輸入和預(yù)期值的數(shù)目(圖1中3~6行的A-H列為空,,是因?yàn)?~6行對應(yīng)的測試方法全部采用公共輸入數(shù)據(jù))。測試數(shù)據(jù)的最后一列為測試說明,,用于對測試條件等輔助信息加以說明,,以供測試人員或維護(hù)人員參考,測試腳本不讀取該列數(shù)據(jù),。
1.2 ini文件的設(shè)計(jì)
使用ini文件是為了使這一實(shí)現(xiàn)方法具有較高的靈活性,,最大限度地減少因測試環(huán)境和測試數(shù)據(jù)的變化帶來的測試腳本的維護(hù)工作量。這里約定ini文件以標(biāo)記的測試類命名,并且保存于執(zhí)行文件所在目錄下,,這樣可通過編程來動(dòng)態(tài)讀取路徑,。因此,,除了ini文件名外,,其他所有數(shù)據(jù)的來源都是通過測試腳本執(zhí)行動(dòng)態(tài)獲取的,這一實(shí)現(xiàn)方法能達(dá)到較高程度的測試腳本與測試數(shù)據(jù)的分離,。
ini文件設(shè)計(jì)如下:
[FILE]
path= "D:\NUNIT\Moneytest.xls"
sheet= [Moneytest$]
[DATA]
input=8
except=4
[ROWS]
Common=0
BagSimpleAdd=1
BagSubtract=2
BagMultiply=3
BagNegate=4
節(jié)[FILE]用于存放Excel數(shù)據(jù)文件信息,,參數(shù)path的值表示數(shù)據(jù)文件存放的路徑,參數(shù)sheet的值表示測試數(shù)據(jù)所在的工作表名,;節(jié)[DATA]用于存放輸入數(shù)據(jù)和預(yù)期值的數(shù)目,,這里按該測試類中使用輸入數(shù)據(jù)和預(yù)期值數(shù)目最多的方法進(jìn)行設(shè)置,用于動(dòng)態(tài)定義結(jié)構(gòu)體內(nèi)數(shù)組的維數(shù),,這樣定義會(huì)損失一定的數(shù)組空間,,但給測試腳本的編寫帶來了方便;節(jié)[ROWS]用于存放每個(gè)測試方法對應(yīng)的測試數(shù)據(jù)所在的行編號(結(jié)構(gòu)體數(shù)組下標(biāo)),,在各測試方法中通過讀取ini文件中的行編號來取得對應(yīng)的測試數(shù)據(jù),,Common=0表示將公用數(shù)據(jù)設(shè)在數(shù)據(jù)表第一行,讀入到下標(biāo)為0的數(shù)組中,。
2 數(shù)據(jù)驅(qū)動(dòng)測試的實(shí)現(xiàn)
Nunit采用屬性標(biāo)簽來標(biāo)記測試類和方法,其中[TestFixture]用于標(biāo)記測試類,,[Test]、[SetUp],、[TestFixtureSetUp]用于標(biāo)記測試方法,。[SetUp]標(biāo)記的方法是為了避免代碼的冗余,該方法將各測試方法中的重復(fù)代碼提取出來,,組織成一個(gè)共用的方法,,在每個(gè)[Test]標(biāo)記的測試方法執(zhí)行前被執(zhí)行一次,與它成對使用的是[TearDown]屬性,,用來釋放[SetUp]中初始化的變量,。[TestFixtureSetUp]與[SetUp]屬性類似,但此屬性標(biāo)記的方法用來實(shí)現(xiàn)整個(gè)測試類的初始化,,它在整個(gè)測試類執(zhí)行前執(zhí)行一次,,與它成對使用的是[TestFixtureTearDown]屬性,用來釋放[TestFixtureSetUp]中初始化的變量,。
在本設(shè)計(jì)中,,測試數(shù)據(jù)被存儲(chǔ)在外部數(shù)據(jù)文件中,每一個(gè)方法執(zhí)行前都需要外部文件中的測試數(shù)據(jù),,如果將讀取外部文件的操作分別寫到各個(gè)測試方法中,,這無疑會(huì)產(chǎn)生大量冗余代碼,也會(huì)增加I/O方面的開銷;如果將這部分處理寫到[SetUp]屬性方法中,,可減少測試腳本中重復(fù)代碼的數(shù)量,,但I(xiàn)/O方面的開銷與前一種方法相比,沒有任何改善,。故本設(shè)計(jì)將測試數(shù)據(jù)讀取的代碼編寫在[TestFixtureSetUp]屬性標(biāo)記的方法中,,通過自定義一個(gè)結(jié)構(gòu)體,將測試數(shù)據(jù)一次性讀出并存放于結(jié)構(gòu)體數(shù)組中,,即在整個(gè)測試類的方法執(zhí)行之前執(zhí)行一次測試數(shù)據(jù)的讀取操作,。這樣,既能減少測試腳本中重復(fù)代碼的數(shù)量,也能減少I/O方面的開銷,。
2.1 自定義結(jié)構(gòu)體的實(shí)現(xiàn)
public struct testdata
{
public string[] strIn ;
public string[] strExcept;
}
上面是采用C#定義的結(jié)構(gòu)體testdata,,其中包含兩個(gè)字符串類型的動(dòng)態(tài)數(shù)組,分別用來存放各測試方法的輸入數(shù)據(jù)和預(yù)期結(jié)果,。因?yàn)闇y試類最大的輸入數(shù)據(jù)和預(yù)期結(jié)果數(shù)目在測試用例維護(hù)時(shí)可能發(fā)生變化,,故采用定義動(dòng)態(tài)數(shù)組的方式。
2.2 測試類初始化的實(shí)現(xiàn)
[TestFixtureSetUp]屬性標(biāo)記的方法用于整個(gè)測試類的初始化,,其處理流程設(shè)計(jì)如圖2所示,。
過程①中讀取[FILE]和[DATA]節(jié)中的值,用于定位測試數(shù)據(jù)文件和定義結(jié)構(gòu)體數(shù)組維數(shù),;過程②按行列依次循環(huán),,將數(shù)據(jù)表中的數(shù)據(jù)依次全部讀入結(jié)構(gòu)體數(shù)組;過程③初始化各測試方法的公用數(shù)據(jù),,下面是按照本文提出的方法,,以Nunit自帶的樣例測試腳本MoneyTest.cs為例對公共輸入數(shù)據(jù)進(jìn)行設(shè)定的相關(guān)代碼:
f12CHF = new Money(Convert.ToInt32(tdTest[i].strIn[0]), tdTest[i].strIn[1]);
f7USD = new Money(Convert.ToInt32(tdTest[i].strIn[2]), tdTest[i].strIn[3]);
f14CHF = new Money(Convert.ToInt32(tdTest[i].strIn[4]), tdTest[i].strIn[5]);
f21USD = new Money(Convert.ToInt32(tdTest[i].strIn[6]), tdTest[i].strIn[7]);
fMB1 = new MoneyBag(f12CHF, f7USD);
fMB2 = new MoneyBag(f14CHF, f21USD);
如果約定下標(biāo)為0的結(jié)構(gòu)體數(shù)組中存放公共測試數(shù)據(jù),則在上面的代碼前可直接將i賦值為0,,也可讀取ini文件的Common值來設(shè)定i值,。
2.3 自定義方法的實(shí)現(xiàn)
每一個(gè)方法執(zhí)行之前都需要讀取結(jié)構(gòu)體數(shù)組中對應(yīng)行的數(shù)據(jù),雖然讀取數(shù)據(jù)的代碼可以寫在各測試方法中,,但這無疑會(huì)產(chǎn)生重復(fù)代碼,。通過分析各測試方法讀取數(shù)據(jù)的相似部分,可將這部分代碼寫成一個(gè)公用方法,,[SetUp]中定義的方法能保證各方法執(zhí)行之前自動(dòng)執(zhí)行一次,,但是該方法不能帶參數(shù)。因此,,這里自定義一個(gè)帶參數(shù)的方法供各測試方法調(diào)用,。同樣以Moneybag類的測試為例,該測試類中,,各方法的輸入基本采用公共數(shù)據(jù),,各方法的預(yù)期結(jié)果各不相同,,因此各方法均有一個(gè)讀取預(yù)期結(jié)果的公共處理,將這部分處理用自定義方法實(shí)現(xiàn),,參數(shù)i為各方法對應(yīng)的測試數(shù)據(jù)的數(shù)組下標(biāo),。代碼如下:
private void SetValue(int i)
{
mexcept1 = new Money(Convert.ToInt32(tdTest[i].strExcept[0]), tdTest[i].strExcept[1]);
mexcept2= new Money(Convert.ToInt32(tdTest[i].strExcept[2]), tdTest[i].strExcept[3]);
mbexcept = new MoneyBag(mexcept1, mexcept2);
}
數(shù)據(jù)表中的數(shù)據(jù)全部以字符串類型讀入構(gòu)造體數(shù)組中,使用時(shí),,根據(jù)具體情況將字符串類型轉(zhuǎn)換為對應(yīng)數(shù)據(jù)類型參與測試,。公共處理以外的個(gè)性數(shù)據(jù)讀取可在各測試方法中實(shí)現(xiàn)。
2.4 各測試方法的實(shí)現(xiàn)
將Nunit自帶的樣例測試腳本按照本文提出的方法進(jìn)行改編,,下面是實(shí)現(xiàn)單一貨幣錢包加法的改編腳本(其他方法基本類似):
[Test]
public void BagSimpleAdd()
{
int intI = GetPrivateProfileString("ROWS", "BagSimpleAdd", "", tmpRow, 500, striniPath + "\\moneytest.ini");
int i = Convert.ToInt32(Convert.ToString(tmpRow));
SetValue(i);
Assert.AreEqual(mbexcept, fMB1.Add(f14CHF));
}
利用本文提出的方法對Nunit自帶測試類Monytest.cs中的所有方法進(jìn)行改編,,執(zhí)行結(jié)果與自帶測試類一致,。與原來的方法相比較,,本文提出的方法實(shí)現(xiàn)了測試腳本與測試數(shù)據(jù)的分離,對測試用例的維護(hù)基本可通過對Excel數(shù)據(jù)文件和ini文件的維護(hù)來實(shí)現(xiàn),,從而降低了測試腳本維護(hù)過程中產(chǎn)生的維護(hù)成本,。在后續(xù)的研究中,將根據(jù)本文提出的方法,,進(jìn)一步探索測試腳本自動(dòng)生成的實(shí)現(xiàn),,以進(jìn)一步提高測試腳本的開發(fā)效率。
參考文獻(xiàn)
[1] 劉德寶.軟件測試工程師培訓(xùn)教材[M].北京:科學(xué)出版社,,2009.
[2] 林勤花.使用NUnit在.net編程中進(jìn)行單元測試[J].科技信息,,2008(24):410-411.
[3] 陸復(fù)名. NUnit.NET項(xiàng)目測試點(diǎn)評[J].程序員,2004(11):128-129.
[4] 陳技能.QTP自動(dòng)化測試實(shí)踐[M]. 北京:電子工業(yè)出版社,,2009.
[5] 劉曉丹,,武君勝,劉博.基于數(shù)據(jù)驅(qū)動(dòng)的自動(dòng)化測試平臺(tái)設(shè)計(jì)[J].科學(xué)技術(shù)與工程,,2008,8(3):779-782.