陳 建,沈亞峰,張 誼
(中國工程物理研究院 計算機應用研究所,四川 綿陽 621900)
隨著電子技術的發展,嵌入式軟件規模越來越大,復雜度也越來越高,軟件質量要求也不斷提高,嵌入式軟件的自動化測試[1,2]以其提高測試效率、保障測試質量等優點越來越受到關注。但隨著嵌入式軟件處理能力的增加,其接口類型更多、結構更復雜,主要體現在業務邏輯與接口數據方面,且不同類型的嵌入式軟件接口存在較大差異性,造成測試數據難以統一,為確保測試充分性需構造大量、復雜的測試數據進行測試,為此針對嵌入式軟件測試能夠實現測試數據快速生成與輸入的自動化測試框架顯得更為重要。
嵌入式軟件的輸入數據類型眾多,包括數字信號、模擬信號以及CAN、UART、LAN等總線數據,測試時需對多種激勵信號進行輸入,另外在強流程嵌入式軟件中,其輸入信號之間具有嚴格的時序要求,通常為毫秒級,測試數據的輸入時序控制也成為嵌入式軟件測試過程中的重點與難點。目前成熟的嵌入式軟件的自動化測試框架如CRESTS、AUTOTEST、GESTE[3]等在使用過程中均需編寫復雜的測試腳本以實現測試數據的輸入,使用門檻較高,測試環境搭建周期較長,不利于快速測試,為此本文主要對嵌入式軟件測試框架進行研究,設計一款輕量級嵌入式軟件自動化測試框架以實現測試數據的快速生成與輸入,提高測試效率。
目前,針對嵌入式軟件的測試框架可分為手工測試框架和腳本驅動測試框架。
手工測試框架:手工測試框架主要采用手動輸入的方式將測試數據填寫到測試工具中,再利用工具輸入到被測軟件(如:NetAssist、UartAssist等),測試數據的輸入具有單一性與無序性,需要輸入不同數據時則需要重新填寫數據,在數據量比較大時,需要花費大量時間,另外輸入的時序無法保證,無法用于具有時序要求的嵌入式軟件測試。
腳本驅動測試框架:腳本驅動測試框架可分為線性腳本、結構化腳本、共享腳本、數據驅動腳本、關鍵字驅動腳本以及混合驅動腳本[4,5]。
(1)線性腳本:線性腳本是通過測試工具錄制測試人員操作被測對象的測試動作、輸入數據,并記錄在腳本文件中。該腳本一般采用特定的語言記錄測試動作序列,腳本可進行回放,線性腳本的優點是簡單,通過錄制即可實現,無須編寫代碼,缺點是僅適用于當前被測軟件,不能共享與重用,可維護性差,僅適用于部分功能。
(2)結構化腳本:結構化腳本是對線性腳本的優化,支持通過編程指令控制腳本執行,腳本具有邏輯判斷能力,根據指令判斷腳本執行順序,相對線性腳本在一定程度上靈活性、健壯性得到改善,并可用于重復性的自動化測試,如Python[6]、ATLAS[7]、TTCN[8]、RASL[9]等腳本語言,但結構化腳本將邏輯指令與測試數據融合,當測試數據需要更新時需重新編寫測試腳本,測試腳本的開發工作量大幅度增加,另外測試場景越復雜,腳本編寫難點越高,維護代價越大,不利用測試工具的普及與應用。
(3)共享腳本:共享腳本在結構化腳本的基礎上對腳本進行完善,使腳本可復用,節省了軟件測試的時間,但測試數據仍未從腳本中進行分離,當多個測試用例需要復用同一測試腳本時,腳本開發、維護工作增加。
(4)數據驅動腳本:數據驅動測試將測試輸入存儲在獨立的數據文件而不是與測試腳本緊耦合。腳本中只存放控制信息,執行測試時從外部數據文件中讀取測試數據,一個測試腳本可以運行不同的測試,避免腳本驅動測試頻繁修改測試腳本的不足。數據文件單獨存儲且可讀性、可維護性高,數據與腳本的分離減少測試腳本的維護成本,提升測試效率[5],但數據驅動腳本測試框架最大限制是數據格式單一、固定,無法適應數據動態變化的情況。
(5)關鍵字驅動腳本:關鍵字驅動腳本在采用數據文件描述測試用例的同時,通過一系列關鍵字描述測試任務,同樣實現了測試腳本與測試數據的分離,但僅通過關鍵字驅動會隨著軟件復雜度增加而增加大量關鍵字,構建成本較大。
(6)混合驅動腳本:采用多種腳本技術協同使用,可以有效避免單一腳本帶來的局限性,彌補單一腳本的不足。
根據以上分析,數據驅動與關鍵字驅動腳本測試框架均實現了數據與腳本的分離,僅通過構建測試數據即可實現不同測試數據的輸入,無需重復編寫測試腳本,提升了腳本的復用性、維護性,國內外基于該技術也做了很多研究[10-15]。
文獻[11]采用基于模型思想,提出一種字符型驅動形式化描述通用方法,建立基于時間的PWM驅動模型實現嵌入式軟件接口驅動的分析與驗證,但該方法僅能用于單一的接口驗證,無法用于軟件的業務流程的測試。文獻[12]將BDD的思想應用于數據驅動的測試中,使用通用的自然語言描述測試用例,該思想注重從用戶的角度進行測試,針對復雜的測試場景,其測試場景與步驟的描述顯得比較繁瑣,需花費大量時間。文獻[13]采用數據驅動方式實現網口的自動化測試,通過數據文件實現測試數據的注入,但當被測對象輸入數據動態變化時無法自適應輸入且不支持其它接口。文獻[14]與文獻[15]采用數據驅動思想實現了自動化測試框架,可通過文件快速構建測試數據,實現測試數據的自動化注入,提高測試效率,但該框架僅對Web軟件數據進行分析,構建數據模型實現Web軟件自動化測試,無法用于嵌入式軟件的強時序復雜接口數據的注入。
綜上分析,本文介紹了一種將數據驅動與關鍵字驅動相結合的混合腳本測試框架,在該框架下即可實現嵌入式軟件測試數據按照前置條件、時序快速主動與被動輸入,也可利用關鍵字實現測試數據的動態自動生成。框架設計主要存在以下難點:①測試數據復雜多樣性;②測試數據動態自適應。
搭建嵌入式軟件自動化測試框架的重點是要快速構建該嵌入式軟件的數據流程以及行為參數[16]。隨著嵌入式處理器復雜與多樣化,嵌入式軟件需要的測試數據也越來越復雜,為滿足不同嵌入式軟件的測試,采用數據驅動的思想構建統一的數據模型表達測試數據,以實現數據的快速輸入。
另外,根據被測軟件業務要求存在動態變化的測試數據,如握手數據、連續數據、離散數據等,為確保對測試數據的自適應,采用關鍵字驅動的思想構建關鍵字模型以表征動態數據,用于實現測試數據自動生成。
最后對測試框架進行設計與實現,并通過實例對框架的功能、性能進行驗證與分析。
基于數據與關鍵字驅動的測試框架是通過數據文件讀取輸入的測試數據與關鍵字,然后傳入測試腳本中,由測試平臺調用測試腳本解析測試數據并按照時序向被測對象輸入測試數據。為確保測試輸入的高效與統一需建立通用的數據模型對測試數據結構進行描述。測試數據模型是抽象出來獨立于測試平臺的測試數據,存儲于結構化的文件中,通過標簽屬性,獲取這些文件中符合條件的指令和配置數據。本文介紹的數據與關鍵字驅動測試框架的數據模型包含數據驅動模型與關鍵字驅動模型。
通過對嵌入式軟件輸入數據的規則與要素進行分析,構建通用輸入數據模型。
定義1 輸入數據模型 (SD):SD=
Port:表示數據輸入的端口類型,包括:LAN、CAN、UART、1553B等;
Ts:表示數據輸入的延時時間,用于控制數據輸入時序;
R:表示數據輸入的次數,用于表示單次數據、周期數據;
Tc:表示數據輸入間隔時間;
Prot:表示數據協議要求,根據數據源的不同分別按照對應的數據協議要求進行設置,如LAN則表示數據輸入的IP地址與端口號,CAN則表示數據類型、幀ID等;
Data:表示待輸入的數據信息,包含固定數據(FD)與關鍵字數據(KD)。
根據輸入數據是否存在前置條件可分為主動輸入數據與被動輸入數據。
主動輸入數據:主動輸入數據即測試平臺通過測試腳本解析數據文件后,可在任意時刻按照設定的時序要求向被測對象直接輸入的數據,不考慮其前置條件。主動輸入數據(ADChain)是由一系列SD按照時序關系組成的有序集合 {SD1,SD2,…,SDn},針對任意i(1≤i≤n),SDi與SDi+1之間均具有相繼發送的時序關系,通過若干個SD的有序發送即可表達被測對象的主動輸入行為。
被動輸入數據:被動輸入數據即在滿足特定的前置條件后才向被測對象輸入的測試數據,針對嵌入式軟件其前置條件一般為輸出條件。
定義2 前置條件(PC):PC=
定義3 響應數據(RD):RD={SD1,SD2,…,SDn},一個響應數據是由多個輸入數據構成的集合,根據前置條件觸發的次數依次進行輸入。
單條被動數據由一個前置條件與若干個響應數據組成,若干條被動數據的集合則構成整個被動輸入數據(PDChain),即PDChain={{PC1,RD11,RD12,…,RD1m},…,{PCn,RDn1,RDn2,…,RDnm}},其中n為被動數據條數,m為響應個數。當獲取到被測對象輸出數據后,對PC1~PCn進行遍歷,若判斷結果PCResi=1(其中1≤i≤n)則將響應數據RDi1~RDim按照設定的時序注入到指定的目標端口即完成被動數據的輸入。
在數據輸入過程中存在變化的動態字段,針對動態字段若僅依靠數據驅動則需要構建大量的數據,花費大量時間,另外在被動輸入數據中存在隨目標數據變化的字段(如握手信息、校驗碼等),數據驅動無法預知其具體值,無法注入滿足要求的數據,為此在數據驅動模型中加入關鍵字,通過數據與關鍵字驅動相結合的方式實現測試數據的自動生成與輸入。
根據嵌入式軟件輸入數據的特點從數據位置、長度、類型、排列方式等角度進行分析,構建關鍵字模型。
定義4 關鍵字模型(KD):KD=
Key:表示數據生成方式,包括握手數據、時間數據、連續數據、離散數據、校驗數據、特殊功能數據(如長度)等;
Type:表示數據類型,包括有符號整形、無符號整形、浮點型等;
BW:表示數據位寬,BW∈{8,16,24,32,…};
Mode:表示數據排列方式,包括小端排列與大端排列;
Pos:表示數據的范圍或數據的起始與終點位置;
Exp:表示數據計算表達式f。
測試數據生成規則如下:根據Key獲取生成數據的類別,選擇相應的生成方式;根據Pos表示的范圍或區間位置獲取初值,根據選擇的生成方式與f計算當前數據值;根據Type、BW以及Mode將計算值填充至指定的字段域。
以數據模型為基礎對該測試框架整體架構進行設計,采用模塊化的思想對各個模塊進行設計,保證模塊的獨立性與可擴展性。框架整體結構主要包括數據解析模塊、被動輸入模塊、主動輸入模塊、接收數據模塊、發送數據模塊以及顯存模塊,具體組成如圖1所示。

圖1 自動化測試框架整體架構
基于上述設計方案,采用VC6.0研制了該測試框架,采用多線程與環形緩存原理保證各個模塊之間既能協同工作,又能實時數據傳遞,框架運行界面如圖2所示。

圖2 自動化測試框架運行界面
其中數據解析模塊與數據輸入模塊是該框架的關鍵模塊,是數據與關鍵字驅動的測試數據輸入思想的具體實現。
3.2.1 數據解析模塊
該框架以文本文件作為測試數據的輸入,在文本文件中存儲待輸入的主動數據與被動數據,軟件按照設定的規則對文件進行解析,形成主動輸入數據鏈(ADChain)與被動輸入數據鏈(PDChain),其中ADChain解析實現過程如下。
Algorithm:LoadADChain
Input:(strPath) /*Active Test file Path*/
Output:(count) /*The count ofADChain*/
(1)p_file = OpenFile(strPath,"r");/*open file*/
(2)whileget a line from p_file and line is not nulldo/*read a line*/
(3) SplitStringToArray(strArray,line," ");/*split the line by space*/
(4)ifstrArray.GetSize() <4then/*check the line*/
(5)exitwhile;
(6)end
(7) pSD = ADChain.AddSDList();/*createSD*/
(8) …/*read thePort、St、Rn、Pre…*/
(9) AnalysisProtocol(strArray[4],pSD); /*analysis and update the protocol*/
(10) SplitStringToArray(strArray,line.right(strArray[5])," ");/*split the data*/
(11)fori ← 0 to strArray.GetSize()-1do/*analysis the keyword data and fix data*/
(12)ifis theKDthen/*isKD*/
(13) pKD = pSD.AddKDList();/*createKD*/
(14) pKD.AnalysisKD(strArray[i]);/*analysis theKD*/
(15) …/*update theKD*/
(16) pSD.Data = 0;/*fill the 0*/
(17)else/*is FData*/
(18) pSD.Data = strArray[i];/*fill the fix data*/
(19)end
(20)end
(21)end
(22)returnADChain.count;
PDChain解析實現過程如下。
Algorithm:LoadPDChain
Input:(strPath) /*Passive Test file Path */
Output:(count) /*The count ofPDChain*/
(1)p_file = OpenFile(strPath,"r");/*open file*/
(2)PDNum = GetPDCount();/*get thePDCount from file*/
(3)fori ← 0 to PDNum-1do
(4) pPD = PDChain.AddPDList();/*createPD*/
(5) pPD.PC = GetPC(p_file);/*read thePC*/
(6)forj ← 0 to RDNum-1do/*read theRD*/
(7) pRD = pPD.AddRDList();/*createRD*/
(8)fork ← 0 to SDNum-1do
(9) pSD = pRD.AddSDList();/*createSD*/
(10) …/*read thePort、St、Rn、Pre…*/
(11) AnalysisProtocol(p_file,pSD);/*analysis and update the protocol*/
(12) …/*analysis the keyword data and fix data*/
(13)end
(14)end
(15)end
(16)returnPDChain.count;
數據解析與顯示界面實現結果如圖3所示。

圖3 數據解析與顯示界面
3.2.2 數據輸入模塊
數據輸入模塊按時間序列對各輸入數據獨立進行管理,當數據輸入時刻到達后根據關鍵字自動生成測試數據并注入到指定的端口,包括數據輸入時刻計算與數據自動生成。
數據輸入時刻計算:針對任意輸入數據,其數據輸入時刻Ti的計算如下
Ti=T0i+Tsi+j×Tci
(1)
式中:i∈[1,n],j∈[0,Ri],n表示輸入數據的個數,Ri表示第i個數據輸入次序,T0i表示相對零點時刻的間隔時間,Tsi表示數據輸入的延時時間,Tci表示數據輸入的間隔時間。針對T0i的計算,主動數據與被動數據計算方式不同。
主動數據:主動數據無前置條件,嚴格按照時序執行,無須等待,故T0i=0;
被動數據:被動數據存在前置條件,需等待前置條件滿足,故當第i個被動數據的前置條件滿足后以當前時刻t為T0i,即T0i=t,在T0i未取值前不進行Ti的計算。
當Ti=t時將計算后的數據注入到指定端口進行發送,隨后j=j+1,繼續計算Ti直到j>Ri則表示該數據輸入完成。
數據自動生成:數據的生成包括固定數據(FD)生成與關鍵字數據(KD)生成,生成的數據為多個十六進制數據組成的集合 {d1,d2,…,dn}。固定數據直接取值即可,即di=FDi,其中di表示第i個字段。
根據關鍵字數據生成規則,其生成過程如下:
初值計算:根據關鍵字中的Key與Pos計算初值y0,具體如下:
握手數據:直接從接收數據中取值,即y0=Rs~Re,其中R表示接收數據集合,s與e是Pos中表示的數據起始與終點位置。
時間數據:直接根據時間規則計算時間,即y0=t,其中t可表示當前日期、時間等。
連續數據:從數據范圍的s→e逐次獲取數據,并逐次加1,即
(2)
離散數據:直接從Pos表示的序列中獲取數據,即y0∈{Pos1,Pos2,…,Posn},根據發送次數逐次獲取。
校驗數據:計算指定區間s→e字段的校驗值,即y0=Fc{ds,ds+1,…,de},其中Fc表示校驗運算,包括和校驗、CRC校驗、異或校驗等。
特殊功能數據:根據功能字段直接計算數據,如長度關鍵字(len)則表示取發送數據長度,即y0=n,其中n表示發送數據長度。
表達式計算:將計算的初值y0與表達式f進行加權計算y=f1×y0+f2,其中f1為縮放表達式,用于對初值進行縮放,f2為偏移表達式,用于對初值進行偏移修正,支持線性與非線性表達式。

再根據Mode填充數據,填充序列如下
(3)
(4)
數據輸入模塊的實現過程如下。
Algorithm:SendData
Input:(rxd,t)/*The receive data rxd,The time t*/
Output:(txd) /*The send data txd*/
(1)txd= null;/*set the txd is null*/
(2)/*check the the Passive data timing*/
(3)fori ← 0 to PDChain.Countdo
(4) PCRes[i]=CheckPC(PDChain[i],rxd);/*checkPC*/
(5)ifPCRes[i]== 1then
(6) T0[i]=t;/*update theT0*/
(7) …/*add toSChain*/
(8)end
(9)end
(10)/*check Ti and create txd*/
(11)fork ← 0 to SChain.Countdo/*include theADChainandPDChainwhich the T0≠0*/
(12)ifT[k]== tthen/*timing is coming*/
(13)fori ← 0 to SD.Countdo/*calculate data*/
(14)ifSDisFDatathen
(15)txd[i]= FD[i];
(16)else/*isKData*/
(17) y0=Calcy0(KD[i]);/*calculate y0*/
(18) y=Calcy(KD[i],y0);/*calculate y*/
(19)txd=FillData(KD[i],y);/*Fill Data*/
(20)end
(21)end
(22) SChain[k].inx++;/*send sequence*/
(23)ifSChain[k].inx <= SChain[k].Rthen
(24) T[k]=T0[k]+SChain[k].Ts+ SChain[k].inx × SChain[k].Tc;/*calculate the nextTi*/
(25)else
(26) …/*remove the SChain[k]fromSChain*/
(27)end
(28)end
(29)end
(30)returntxd;
通過某監控嵌入式軟件的狀態查詢與靜檢功能對該框架進行驗證,監控軟件運行后周期接收下位機發送的自檢結果“55 AA 01 …”與狀態信息“AA/BB 7E …”并進行解析,另外可主動發送靜檢命令“55 AA 03 …”、“55 81 02 00”,待下位機收到靜檢命令后被動反饋應答“55 AA/BB …”及靜檢結果“AA 7E …”。首先根據監控軟件狀態查詢與靜檢功能的輸入輸出數據流構建如圖4需要發送的輸入數據文件。

圖4 輸入數據文件
其中字段“len[]”表示特殊功能數據的長度數據,取數據字段的總長度;字段“time32[]”表示時間數據,取系統當前時間;字段“cnt16[0~10]*10”表示連續數據,數據范圍為[0~100],間隔10;字段“sum8[0~len-2]”表示校驗數據,計算指定區間數據的和校驗;字段“rec[3~4]”表示握手數據,表示取接收數據的第3~4字節。
運行該測試框架,讀取數據文件,發送數據,框架實例化各關鍵字生成相應的測試數據并按照時序逐次向監控軟件發送數據,具體結果如圖5所示。

圖5 主動發送數據結果
另外對監控軟件發送的目標數據進行接收,當接收到靜檢命令后進行前置條件判斷,滿足要求后生成測試數據并按照時序要求向監控軟件進行被動發送,具體結果如圖6所示。

圖6 被動發送數據結果
對發送的數據的時間性能進行分析,具體見表1。

表1 數據發送時間測試結果/ms
另外,再通過其它工具實現圖4中的數據發送行為,其操作結果對比見表2。

表2 不同類型工具操作結果對比分析
根據測試結果可知,該自動化測試框架能夠正確解析數據文件,實現測試數據的主動與被動輸入,自動實例化關鍵字并生成測試數據進行發送,發送數據內容、時序正確,數據處理效率在毫秒級,能夠滿足嵌入式軟件數據處理實時性要求。
本文將數據與關鍵字驅動思想應用到嵌入式軟件測試中,設計并研制了一款輕量級嵌入式軟件自動化測試框架。相較于傳統嵌入式軟件測試框架,該框架能夠控制測試數據的輸入時序與條件,同時可實現測試數據的自適應生成;相較于專業嵌入式軟件測試框架,該框架將測試數據與測試腳本完全分離,省略了測試過程中復雜的測試腳本編寫過程,降低了測試環境搭建時間與難度,另外簡明、格式化的數據結構能夠使測試人員快速構造測試數據進行測試,提升了測試效率,工程實用性較強。
根據實例驗證可知,該框架可用于嵌入式軟件測試,并已在多個嵌入式軟件測試中進行應用。但該框架目前測試數據自動生成策略尚未結合測試設計方法,后續將測試設計方法融合其中實現基于模型的測試數據自動生成,以適應復雜系統的測試。