單元測(cè)試如何改良項(xiàng)目代碼的整體結(jié)構(gòu)

字號(hào):

具有良好整體結(jié)構(gòu)的代碼,應(yīng)該符合“低耦合”的特性,形象的說(shuō),就是“各家自掃門前雪、不管他人瓦上霜”,每一個(gè)函數(shù)、每一個(gè)類、每一個(gè)模塊,都應(yīng)該只做自己該做的事,不要把應(yīng)該由“其他人”做的事扯進(jìn)來(lái)。具有良好整體結(jié)構(gòu)的代碼就具有“可測(cè)性”,否則就不具有“可測(cè)性”。
    系統(tǒng)分析和架構(gòu)設(shè)計(jì)做得比較好的項(xiàng)目,所實(shí)現(xiàn)的代碼一般具有比較好的整體結(jié)構(gòu),應(yīng)該首先在這方面下功夫。另一方面,單元測(cè)試是“隔離”的測(cè)試,可以說(shuō),“隔離”是單元測(cè)試的最基本特性,不能“隔離”的代碼,單元測(cè)試也就難于進(jìn)行。通常,“低耦合”的代碼可以隔離,具有不當(dāng)?shù)母唏詈咸匦缘拇a難于隔離,因此,單元測(cè)試能夠及時(shí)地發(fā)現(xiàn)不當(dāng)耦合,推動(dòng)代碼重構(gòu),從而保證了代碼具有良好的整體結(jié)構(gòu)。
    具體來(lái)說(shuō),如果代碼包含不當(dāng)耦合,當(dāng)這些代碼加入測(cè)試工程后,會(huì)產(chǎn)生編譯錯(cuò)誤,或者需要打樁才能測(cè)試,從而將不當(dāng)耦合暴露出來(lái)。發(fā)現(xiàn)問(wèn)題后,重構(gòu)代碼、消除不當(dāng)耦合一般不難,消除不當(dāng)耦合后,單元測(cè)試就可以順利進(jìn)行了。
    下面是幾種典型的不當(dāng)耦合:
    把代碼寫(xiě)在界面類中
    問(wèn)題:如果把業(yè)務(wù)代碼寫(xiě)在界面類中,測(cè)試時(shí)把界面類加入測(cè)試工程,會(huì)產(chǎn)生編譯錯(cuò)誤。
    解決:把業(yè)務(wù)代碼獨(dú)立出來(lái),寫(xiě)到相應(yīng)的實(shí)體類中,對(duì)這些實(shí)體類進(jìn)行測(cè)試。界面類只負(fù)責(zé)數(shù)據(jù)的顯示和接受用戶的輸入,具體的計(jì)算由實(shí)體類負(fù)責(zé)。
    說(shuō)明:把業(yè)務(wù)代碼寫(xiě)在界面類中,這些代碼將很難管理和維護(hù),復(fù)用就更談不上了,這是一定要避免的。采集者退散
    實(shí)體類混合了邊界代碼
    問(wèn)題:例如,一個(gè)表示用戶的類CUser,它的對(duì)象的數(shù)據(jù)保存在數(shù)據(jù)庫(kù)中,如果在CUser類中直接讀寫(xiě)數(shù)據(jù)庫(kù),就是實(shí)體類混合了邊界代碼,這也是一種不當(dāng)耦合,測(cè)試CUser類時(shí)要察看數(shù)據(jù)庫(kù),或者要打樁,甚至?xí)a(chǎn)生編譯錯(cuò)誤。
    解決:把執(zhí)行數(shù)據(jù)庫(kù)操作的代碼寫(xiě)到CDatabase類中,CUser類和CDatabase類沒(méi)有任何關(guān)聯(lián),由界面類或用于協(xié)調(diào)各對(duì)象工作的控制類來(lái)操作這兩個(gè)類的對(duì)象進(jìn)行數(shù)據(jù)讀寫(xiě),現(xiàn)在對(duì)CUser類的測(cè)試就完全和數(shù)據(jù)庫(kù)無(wú)關(guān)了。
    說(shuō)明:經(jīng)過(guò)重構(gòu)后,代碼的可擴(kuò)展性、可復(fù)用性都有了很大提高,也便于進(jìn)行單元測(cè)試和將來(lái)的維護(hù)。考試大論壇
    無(wú)意中形成了高耦合
    問(wèn)題:例如,CMyClass類中一個(gè)函數(shù)中有這樣的語(yǔ)句:
    CDemoApp* pApp = (CDemoApp*)AfxGetApp();
    UNIT var = pApp->GetXXX()->Getxxxx();
    由于CDemoApp是工程的層類,可能跟工程中的所有類關(guān)聯(lián),這兩行代碼就造成被測(cè)試對(duì)象跟工程中的所有類耦合。耦合具有擴(kuò)散性,縱向來(lái)說(shuō),CMyClass類的所有子類也可能跟工程中的所有類關(guān)聯(lián),橫向來(lái)說(shuō),所有使用了CMyClass類的類,也可能跟工程中的所有類關(guān)聯(lián),從而形成了盤(pán)根錯(cuò)節(jié)的關(guān)系。這種問(wèn)題往往很難發(fā)現(xiàn),但把代碼加入測(cè)試工程后,一般來(lái)說(shuō)是通不過(guò)編譯的,從而使問(wèn)題暴露。來(lái)源:考試大
    解決:修改被測(cè)試代碼,去除耦合。把需要從pApp指針中取得的數(shù)據(jù)改為由參數(shù)傳進(jìn)來(lái),這兩行代碼移到客戶程序(即調(diào)用被測(cè)試函數(shù)的代碼)中,如果客戶程序也是實(shí)體類,則繼續(xù)往上移,直到界面類。
    說(shuō)明:這類無(wú)意中形成的耦合非常常見(jiàn),一般的代碼審查、靜態(tài)掃描都很難發(fā)現(xiàn)這類問(wèn)題,但把代碼加入測(cè)試工程后,編譯器卻能輕松發(fā)現(xiàn)。這類問(wèn)題只要及時(shí)發(fā)現(xiàn)了,消除并不難,經(jīng)過(guò)簡(jiǎn)單的代碼重構(gòu)后,就可以有效地提高代碼質(zhì)量,特別是提高代碼的可復(fù)用性,也使單元測(cè)試可以順利進(jìn)行。
    開(kāi)發(fā)工具生成的代碼形成了不當(dāng)耦合
    有時(shí)候,開(kāi)發(fā)工具生成的代碼也形成了不當(dāng)耦合,例如,VC6.0生成的類代碼中,源文件會(huì)添加下面的代碼:#include "ProjectName.h" //ProjectName是工程名
    如果在另一個(gè)不同名的工程中復(fù)用代碼,這一行就會(huì)產(chǎn)生編譯錯(cuò)誤。進(jìn)行單元測(cè)試時(shí),這一行形成的不當(dāng)耦合也可能會(huì)導(dǎo)致編譯錯(cuò)誤,因此應(yīng)刪除它。