摘 要:信息化飛速發展,需求隨之變動,現今社會對軟件產品質量的要求逐漸提高。該文首先介紹傳統軟件開發和敏捷軟件開發體系,然后對比分析瀑布模型與極限編程,測試驅動開發與傳統軟件測試、V模型的各自特點,并結合實際,具體闡釋敏捷軟件開發方法在C語言符號表的構造實現,最后從測試覆蓋率方面評估此次測試集。從測試評估報告結果看,采用先測試再編碼步驟的測試驅動開發TDD方法開發的軟件,不僅測試更為完備、徹底,而且更適用于需求變動的環境。
關鍵詞:軟件質量;敏捷軟件開發;極限編程;符號表;測試驅動
中圖分類號:TP311.5 文獻標志碼:A 文章編號:2095-2945(2024)27-0018-04
Abstract: With the rapid development of information technology, the demand changes accordingly, and the requirements for the quality of software products are gradually improving in today's society. This paper first introduces the traditional software development and agile software development system, then compares and analyzes the respective characteristics of waterfall model and extreme programming, test-driven development, traditional software testing and V model, and combines with practice. The construction and implementation of agile software development method in C language symbol table is explained in detail, and finally the test set is evaluated in terms of test coverage. According to the results of the test evaluation report, the software developed by the test-driven method of test-driven development with the first test and then coding steps is not only more complete and thorough testing, but also more suitable for the environment of changing requirements.
Keywords: software quality; agile software development; extreme programming; symbol table; test-driven
傳統軟件開發方法,如瀑布模型是一早期軟件開發模型,遵循軟件生命周期從需求分析、設計編碼,運行維護各階段按順序依次進行,如同瀑布自上而下。瀑布模型要求早期詳盡需求分析,需求文檔一旦固定不再改變。當客戶需求發生變更,無疑極大增加了成本,從而影響項目開發進度。
相比較傳統瀑布模型拒絕用戶需求變化,Agile Development敏捷軟件開發是近些年來一種以快速響應用戶需求變化的輕量級軟件開發方法[1]。它強調快速編寫軟件,及時交付軟件小功能模塊,迭代式開發,將用戶一并納入到開發團隊中,包括Rational Unified Process(RUP)、Feature Driven Development(FDD)、Extreme Programming(XP)、Crystal Method等。
1 軟件開發方法分析
在所有敏捷軟件開發方法中,Extreme Programming(XP)極限編程是一種要求協作、迭代、不斷改進的軟件開發方法,強調響應需求變化、及時交付高質量的軟件。
XP克服了傳統瀑布模型過于依賴文檔需求快速響應需求的缺陷,強調積極應對需求變化,整個開發過程通過小規模、迭代式的開發模式快速交付系統部分功能,積極適應外部環境變化,并以測試先行、持續集成,極大提高了軟件質量和可靠性,從而減少了軟件錯誤、缺陷。
相比傳統瀑布模型先編碼再軟件測試,上一個階段結束才能開始下一個階段的線性模式,極限編程倡導測試先行,即先編寫測試用例代碼,再編寫通過測試的實際代碼,迭代式推進整個開發過程。瀑布模型以早期需求文檔為中心,用戶無需參與開發全程,這往往導致最后交付的軟件不滿足用戶實際需要。極限編程則以溝通為核心價值,倡導用戶參與開發過程,積極響應用戶需求,在最短的時間內,小型、頻繁發布系統部分功能。
2 測試驅動開發
Test-Driven Development(TDD)測試先行開發是極限編程XP的核心技術之一。它不是一種軟件測試方法,而是一種獨立的軟件實踐方法,用測試驅動和指導設計編碼,將單元測試和程序設計結合在一起[2],為軟件的可靠性和質量提供支撐。
整個開發過程需要一套在自動化測試框架下自動運行完備的測試集,步驟為先黃、后紅、再綠,好比交通燈交替顯示。在具體編碼之前需要先編寫測試代碼,由測試來指導需要什么樣的代碼[3]。通過測試只是測試驅動開發的前提,它還強調重構性,在測試通過后需對代碼進行重構,并且重構后的功能代碼也需要通過測試。按照以上步驟開展,直到系統所需功能全部通過測試。
2.1 測試驅動與傳統軟件測試分析
由表1可知,傳統軟件測試往往是在設計開發完成后由專門的軟件測試員負責完成,而測試員需要花費時間和精力去理解代碼,容易造成理解不到位而遺漏某些測試。而測試驅動開發就不存在這樣的情況,代碼基于測試,均由程序員完成,節約時間成本。當發現錯誤時,傳統軟件測試是滯后的,很難快速定位錯誤位置。而在測試驅動開發中,系統增添新代碼引入bug,測試集能快速定位bug位置,從而大大減少了調試時間。
2.2 測試驅動開發與V模型對比
V模型強調測試及驗證在軟件開發過程的重要,其實施步驟如V字母,一邊為開發流程,另一邊為測試流程,每一個階段的輸出都有與之對應的測試階段。
雖然測試驅動開發與V模型都主張在具體編碼之前先設計好測試用例,但區別點是V模型只是設計測試用例在編碼之前,而創建測試用例及執行測試用例仍在編碼之后,但在測試驅動開發中,設計、創建及執行測試用例均在實際編碼之前[4]。二者的執行力度也不同。相比較V模型,測試驅動開發要求測試先行,測試用例運行要比V模型運行更頻繁。
3 符號表設計
本文以測試驅動開發實現在C語言編譯過程符號表的設計構造實現為例進行介紹。
3.1 語言特點分析
編譯為將源程序翻譯成目標語言的過程,其過程可以是多趟掃描或一趟性掃描源程序。無論多趟還是一趟,作為收集和存儲在源程序中標識符的相關屬性信息,符號表生成構造是整個編譯過程必不可少的。編譯過程符號表中的相關信息是動態變化、不斷更新的。
編譯第一階段為詞法分析,即從源程序識別出的單詞符號。在C語言,單詞符號分為變量、常量、關鍵字、算符和界符等。變量為字母數字下劃線組成的符號串。關鍵字有以下類型:數據類型如int、控制語句類的如while、存儲類型的如static,以及其他類如typedef等。算符有雙目、單目、關系與邏輯運算符等。界符分為()、{ }、[ ]等。常量包括整型或實型數值型、字符型等。
在遍歷源程序過程中,對識別出的一個個單詞,本文依據單詞類別,不同種類的單詞分門別類存儲在不同的符號表中,而不是所有單詞信息存在一張符號表中。對于單詞信息,本文采用三元組形式(類別,名稱,屬性值)記錄單詞相關信息,使用數值來區分不同類別,即一類別一數值。如關鍵字類別數值為1,變量類別數值為2,常量類別數值為3……依次類推。根據C語言特點,這里,符號表關鍵字、常量、算符、界符類別的單詞存儲在靜態符號表中,而變量類別的存儲在動態符號表中。在動態符號表中,依據變量類別的不同分別存儲,如全局變量存儲在全局變量符號表中,局部變量存儲在局部變量符號表中。
3.2 符號表設計
在本文中,不同單詞種類的符號表均涉及新增、刪除、查找、修改操作。考慮實例化操作頻繁,這里初始化過程采用Factory工廠設計模式來完成,即不同單詞子類以繼承方式實現父類初始化操作。
如圖1所示,接口父類WordTable包含增修刪及遍歷查找操作方法。這些方法由各派生類符號表在各自操作方法中具體實現。從父類派生的全局符號表GlobalTable類、局部LocalTable類、函數符號表類……依據單詞類別,將識別出的單詞需存儲在不同類符號表中。對于符號表新增節點的初始化操作,這里統一交給操作類NodeCreateFac的Initial()方法完成,使得節點的初始化操作將會動態調用相應符號表的方法。
4 設計實現
4.1 開發環境
本文選取JUnit測試開發環境。JUnit是XUnit測試框架家族成員之一,一款支持自動化回歸測試的單元測試框架,支持單元測試、功能測試、集成測試,支持多語言創建和運行測試,提供多個TestCase測試用例、Assert斷言、Report報告等功能[5]。
JUnit測試框架采用的是組合設計模式。其中Test是接口類,含有Run方法,是運行測試、收集測試結果。測試用例集合TestSuite會調用Run方法依次遍歷所容納的所有從Test派生而來的對象。斷言方法集合靜態類Assert,用于比對期望值和實際值,若是比對失敗則拋出異常。抽象類TestCase則是整個測試框架的核心,我們創建具體測試用例都是通過繼承抽象類TestCase來實現。
4.2 測試驅動實現
本文采用測試驅動開發方法,按照先測試再編碼的步驟逐步實現C語言符號表構造。這部分以符號表初始化為例,介紹測試用例編寫到代碼實現過程。
按照設計階段列出的所有測試任務優先級先后順序創建測試用例。細化測試任務,第一條測試任務為輸入空,輸出空。開始編寫TestCase測試用例,創建測試方法TestEmptySourcePragm,具體如下:
public void testEmptySourcePragm( ) {
LexicalAnalyzer LA= new LexicalAnalyzer("");
assertEquals("Null Result!","",LA.getAnalResult( ));
}
運行測試方法,編譯失敗。產生報錯信息顯示:類LexicalAnalyzer、getAnalResult方法未定義。
原因在于目前還不存在任何代碼,所以也無類LexicalAnalyzer和方法getAnalResult存在。按照TDD開發步驟,接下來依據報錯信息,創建類LexicalAnalyzer 及方法getAnalResult,具體如下:
public class LexicalAnalyzer {
LexicalAnalyzer(String source}{
}
public String getAnalResult{
return ;
}
}
在getAnalResult方法中,我們設置返回結果null。再次運行測試方法。JUnit綠燈出現,編譯通過。然后檢查源代碼是否需要重構,有無重復冗余。按照測試—編碼—重構的步驟完成了第一個測試任務。
接下來,需要按照任務單順序依次測試驅動實現各類符號表的構造。以局部變量符號表構造實現為例,所需要測試任務有節點新增、刪除和修改等。按照從簡單到復雜的測試順序逐步實現并完善代碼功能。首先對局部變量符號表最簡單的測試為當局部變量符號表為空時,返回變量個數為0。先編寫測試用例testLocalTableEmpty:
public void testEmptyVartableSize( ){
LocalTable Lt=new LocalTable(LexicalAnalyzer.getLocalTable( ));
assertEquals("Size of LocalTable is Zero",0,Lt.getSize( ));
}
執行測試用例 testEmptyVartableSize,JUnit顯示黃燈,編譯不通過。依據報錯提示信息,這里需要創建局部變量符號類LocalTable及getSize方法,并且需要在分析器類LexicalAnalyzer中添加相應代碼,使其通過編譯。在getSize方法中依舊采用最簡單的實現方法,讓其返回值為0。
局部變量符號表LocalTable一旦生成后,后續的測試就是圍繞局部變量符號表的基本操作,即增、刪、改。以增加變量節點為例,測試方法如下:
public void testAddOperaofLocalTable( ){
VarNode newVar=new VarNode(2,"ch","a");
Lt.addNewVar(newVar);
assertEquals("Size of LocalTable is 1",1,Lt.getSize( ));
}
新增變量節點newVar后,局部變量符號表的數量應返回1。增加的單詞類型為類型2變量,其名稱為ch,值為a。運行測試用例,JUnit出現紅燈,測試失敗。報錯信息為:“size of localTable is 1,expected :<1>,but<0>”。依據錯誤提示,查找原因,定位在代碼getSize方法中。因為當初為了使其通過編譯采用最簡單的辦法,使getSize方法返回值為0,且addNewVar方法沒有具體內容。接下來,需要在getSize方法和addNewVar方法增添相應代碼實現符號表的新增和個數統計功能, 并且在testAddOperaofLocalTable測試通過后,需要繼續增加變量節點反復測試新增功能是否完善。
同樣,局部變量符號表的測試任務單也少不了對于符號表的遍歷功能的測試,即遍歷符號表,返回它在符號表中所在的位置,若存在期望結果應為真,若不存在期望結果應為假。根據測試任務單編寫測試用例testLocalTableSearch方法,如:
Lt.addNewVar(newVar1);
Lt.addNewVar(newVar2);
assertTrue("newVar1 should be in LocalTable",Lt.SearchVar(newVar1));
assertTrue("newVar2 should be in LocalTable",Lt.SearchVar(newVar2));
assertFalse("newVar3 shouldn't be in LocalTable",Lt.SearchVar(newVar3));
在本測試用例中增加了變量newVar1和newVar2,期望應為真;對于newVar3實際不存在期望應為假。執行測試,運行失敗,編寫具體代碼在LocalTable類中增加遍歷方法SearchVar使其通過測試。測試通過后需檢查代碼是否冗余,若存在則對代碼進行重構。
依照極限編程思路遵循先測試再編碼重構迭代原則依次完成其他類別符號表的構造設計,并將詞法分析輸出的結果分別存儲在對應的符號表中,為語法、語義分析及代碼生成等后續編譯階段的使用提供支撐。
在完成符號表設計構造后,還需進行測試評估。軟件測試分為黑盒、白盒測試。極限編程TDD倡導測試徹底,所以采用更為徹底的白盒測試,即從先編寫測試用例再編碼的循序,對程序內部邏輯進行測試,力求提高測試覆蓋程度。本文采用Cobertura軟件測試覆蓋計算工具對代碼進行全面測試,從語句覆蓋率、語句分支覆蓋率二維角度對程序代碼情況進行綜合評測,并生成測試評估報告。此次的測試覆蓋評估報告顯示Line覆蓋、Branch覆蓋都達95%以上,其中部分Class達到100%。從結果看,測試集比較完備,測試較為充分。
5 結束語
本文對比分析了傳統軟件開發瀑布方法與敏捷軟件開發TDD方法的特點,按照極限編程測試驅動開發思路,測試驅動構造實現C語言符號表,并對代碼進行了測試評估。從測試評估結果得出測試驅動生成的代碼測試更為充分。
參考文獻:
[1] 劉鈺槐.敏捷軟件開發方法在軟件重構中的運用分析[J].通信電源技術,2017,34(6):155-156.
[2] LASSE K.測試驅動開發的藝術[M].李貝,譯.北京:人民郵電出版社,2010.
[3] 林勇.淺談測試驅動開發[J].中國金融電腦,2012(4):48-50.
[4] 陳迪舸.芻議測試驅動開發在軟件開發中的作用[J].電子技術與軟件工程,2016(7):60.
[5] 王孝梅.測試驅動開發的研究[J].電腦編程技巧與維護,2021(2):47-49.