軟件測試中如何使用VC6進(jìn)行單元測試
單元測試(模塊測試)是開(kāi)發(fā)者編寫(xiě)的一小段代碼,用于檢驗被測代碼的一個(gè)很小的、很明確的功能是否正確。通常而言,一個(gè)單元測試是用于判斷某個(gè)特定條件(或者場(chǎng)景)下某個(gè)特定函數的行為。例如,你可能把一個(gè)很大的值放入一個(gè)有序list 中去,然后確認該值出現在list 的尾部;蛘,你可能會(huì )從字符串中刪除匹配某種模式的字符,然后確認字符串確實(shí)不再包含這些字符了。 單元測試是由程序員自己來(lái)完成,最終受益的也是程序員自己?梢赃@么說(shuō),程序員有責任編寫(xiě)功能代碼,同時(shí)也就有責任為自己的代碼編寫(xiě)單元測試。執行單元測試,就是為了證明這段代碼的行為和我們期望的一致。 工廠(chǎng)在組裝一臺電視機之前,會(huì )對每個(gè)元件都進(jìn)行測試,這,就是單元測試。 其實(shí)我們每天都在做單元測試。你寫(xiě)了一個(gè)函數,除了極簡(jiǎn)單的外,總是要執行一下,看看功能是否正常,有時(shí)還要想辦法輸出些數據,如彈出信息窗口什么的,這,也是單元測試,把這種單元測試稱(chēng)為臨時(shí)單元測試。只進(jìn)行了臨時(shí)單元測試的軟件,針對代碼的測試很不完整,代碼覆蓋率要超過(guò)70%都很困難,未覆蓋的代碼可能遺留大量的細小的錯誤,這些錯誤還會(huì )互相影響,當BUG暴露出來(lái)的時(shí)候難于調試,大幅度提高后期測試和維護成本,也降低了開(kāi)發(fā)商的競爭力?梢哉f(shuō),進(jìn)行充分的單元測試,是提高軟件質(zhì)量,降低開(kāi)發(fā)成本的必由之路。 對于程序員來(lái)說(shuō),如果養成了對自己寫(xiě)的代碼進(jìn)行單元測試的習慣,不但可以寫(xiě)出高質(zhì)量的代碼,而且還能提高編程水平。 要進(jìn)行充分的單元測試,應專(zhuān)門(mén)編寫(xiě)測試代碼,并與產(chǎn)品代碼隔離。老納認為,比較簡(jiǎn)單的辦法是為產(chǎn)品工程建立對應的測試工程,為每個(gè)類(lèi)建立對應的測試類(lèi),為每個(gè)函數(很簡(jiǎn)單的除外)建立測試函數。首先就幾個(gè)概念談?wù)劺霞{的看法。 一般認為,在結構化程序時(shí)代,單元測試所說(shuō)的單元是指函數,在當今的面向對象時(shí)代,單元測試所說(shuō)的單元是指類(lèi)。以老納的實(shí)踐來(lái)看,以類(lèi)作為測試單位,復雜度高,可操作性較差,因此仍然主張以函數作為單元測試的測試單位,但可以用一個(gè)測試類(lèi)來(lái)組織某個(gè)類(lèi)的所有測試函數。單元測試不應過(guò)分強調面向對象,因為局部代碼依然是結構化的。單元測試的工作量較大,簡(jiǎn)單實(shí)用高效才是硬道理。 有一種看法是,只測試類(lèi)的接口(公有函數),不測試其他函數,從面向對象角度來(lái)看,確實(shí)有其道理,但是,測試的目的是找錯并最終排錯,因此,只要是包含錯誤的可能性較大的函數都要測試,跟函數是否私有沒(méi)有關(guān)系。對于C++來(lái)說(shuō),可以用一種簡(jiǎn)單的方法區隔需測試的函數:簡(jiǎn)單的函數如數據讀寫(xiě)函數的實(shí)現在頭文件中編寫(xiě)(inline函數),所有在源文件編寫(xiě)實(shí)現的函數都要進(jìn)行測試(構造函數和析構函數除外)。
接下來(lái)給大家講一下有關(guān)單元測試的實(shí)例
多數講述單元測試的文章都是以Java為例,本文以C++為例,后半部分所介紹的單元測試工具也只介紹C++單元測試工具。下面的示例代碼的開(kāi)發(fā)環(huán)境是VC6.0。
產(chǎn)品類(lèi):
class CMyClass
{
public:
int Add(int i, int j);
CMyClass();
virtual ~CMyClass();
private:
int mAge; //年齡
CString mPhase; //年齡階段,如"少年","青年"
};
建立對應的測試類(lèi)CMyClassTester,為了節約編幅,只列出源文件的代碼:
void CMyClassTester::CaseBegin()
{
//pObj是CMyClassTester類(lèi)的成員變量,是被測試類(lèi)的對象的指針,
//為求簡(jiǎn)單,所有的測試類(lèi)都可以用pObj命名被測試對象的指針。
pObj = new CMyClass();
}
void CMyClassTester::CaseEnd()
{
delete pObj;
}
測試類(lèi)的函數CaseBegin()和CaseEnd()建立和銷(xiāo)毀被測試對象,每個(gè)測試用例的開(kāi)頭都要調用CaseBegin(),結尾都要調用CaseEnd()。
接下來(lái),我們建立示例的產(chǎn)品函數:
int CMyClass::Add(int i, int j)
{
return i+j;
}
和對應的測試函數:
void CMyClassTester::Add_int_int()
{
}
把參數表作為函數名的一部分,這樣當出現重載的被測試函數時(shí),測試函數不會(huì )產(chǎn)生命名沖突。下面添加測試用例:
void CMyClassTester::Add_int_int()
{
//第一個(gè)測試用例
CaseBegin();{ //1
int i = 0; //2
int j = 0; //3
int ret = pObj->Add(i, j); //4
ASSERT(ret == 0); //5
}CaseEnd(); //6
}
第1和第6行建立和銷(xiāo)毀被測試對象,所加的{}是為了讓每個(gè)測試用例的代碼有一個(gè)獨立的域,以便多個(gè)測試用例使用相同的變量名。
第2和第3行是定義輸入數據,第4行是調用被測試函數,這些容易理解,不作進(jìn)一步解釋。第5行是預期輸出,它的特點(diǎn)是當實(shí)際輸出與預期輸出不同時(shí)自動(dòng)報錯,ASSERT是VC的斷言宏,也可以使用其他類(lèi)似功能的宏,使用測試工具進(jìn)行單元測試時(shí),可以使用該工具定義的斷言宏。
示例中的格式顯得很不簡(jiǎn)潔,2、3、4、5行可以合寫(xiě)為一行:ASSERT(pObj->Add(0, 0) == 0);但這種不簡(jiǎn)潔的格式卻是我們推薦的,因為它一目了然,易于建立多個(gè)測試用例,并且具有很好的適應性,同時(shí),也是極佳的代碼文檔,總之,我們建議:輸入數據和預期輸出要自成一塊。
建立了第一個(gè)測試用例后,應編譯并運行測試,以排除語(yǔ)法錯誤,然后,使用拷貝/修改的辦法建立其他測試用例。由于各個(gè)測試用例之間的差別往往很小,通常只需修改一兩個(gè)數據,拷貝/修改是建立多個(gè)測試用例的最快捷辦法。
文章來(lái)源于領(lǐng)測軟件測試網(wǎng) http://kjueaiud.com/