錢漢偉
(江蘇警官學院 計算機信息與網(wǎng)絡安全系,江蘇 南京 210031)
圖形用戶界面(Graphical User Interface)的出現(xiàn)是軟件發(fā)展歷程中一個重要的里程碑,它給用戶帶來了極大的方便。圖形用戶界面是用戶與系統(tǒng)進行交互的接口,接受用戶或者其他系統(tǒng)輸入事件,產生圖形化的輸出,用戶對系統(tǒng)功能的調用和系統(tǒng)對用戶的反饋都是通過GUI進行的。GUI軟件接口越來越復雜,GUI部分占據(jù)了應用程序越來越多的代碼量,相應的GUI軟件測試也成了一項復雜而煩瑣的工作。在軟件復雜度增加的同時,軟件開發(fā)周期卻在不斷變短,這給測試人員帶來了巨大的挑戰(zhàn)。
減少重復勞動,節(jié)省人力、時間,提高測試效率,減少人工測試中的錯誤,更好的協(xié)調開發(fā)工作量與測試工作量,進行GUI自動化測試是軟件測試發(fā)展的一個必然趨勢。
經(jīng)過很多年的發(fā)展,目前已經(jīng)有了相對成熟的GUI自動化測試工具了。這些工具有著自己的優(yōu)點,同時也有一定的應用局限性。
SilkTest是Segue公司的GUI測試工具[1]。它運行在Windows平臺,可以查詢和測試使用標準MFC庫生成的對象和GUI部件,也可以使用一些擴展的功能來測試非 MFC的 GUI部件。進行測試時,SilkTest提供捕捉/回放功能,能夠與被測應用進行手工交互。一旦用戶選擇了待測的 GUI部件,SilkTest可以檢查該部件的標識符、位置和物理標簽。在用戶繼續(xù)操作應用時,SilkTest將用戶動作翻譯成基于對象屬性或部件屏幕坐標的測試腳本。WinRunner是惠普公司一款圖形界面自動化測試工具。用戶可以簡單的通過錄制/回放的特點來完成一個測試用例。在錄制過程中,它可以自動捕獲檢測當前的界面。把用戶在界面上的操作自動轉換成可描述性的語言和事件。QTP(Mercury QuickTest Professional)主要用于回歸測試和同一軟件的新版本的測試,在自動化測試工具中,QTP是一個典型的代表。
開源的 Java GUI自動化測試工具也有很多。Abbot框架能夠對java應用進行GUI單元測試和功能測試[2]。它支持用戶使用java代碼編寫測試腳本,也提供接口,讓用戶通過腳本來控制事件回放,以加強集成測試和功能測試。GUITAR是個GUI測試框架,提供解決GUI測試問題的統(tǒng)一的解決方法,該工具著力于開發(fā)新的基于事件的工具和技術。它包含一個測試用例生成器,可以生成測試用例,一個插件重放器負責在這些測試用例中運行測試。Pounder是實現(xiàn)自動java GUI測試的開放源碼的工具。它包括不同的窗口,用戶可以用來記錄測試腳本,檢查測試結果。Selenium是一個用于Web應用程序測試的工具[3],它的測試直接運行在瀏覽器中,就像真正的用戶在操作一樣。
很多開源的GUI自動化測試框架都是基于java的,其中對于Swing窗口工具組,它的組件自動產生各種事件來相應用戶行為,通過注冊監(jiān)聽器,我們可以監(jiān)聽事件源產生的事件,從而在事件處理程序中處理我們所需要處理的用戶行為。但是Java運行在虛擬機上,對于更廣泛的Windows的GUI測試沒有太多的借鑒意義。大部分通用的自動化測試工具,如QTP,通常只能識別Windows標準的控件(按鈕,滾動條,靜態(tài)控件, 列表框, 編輯框, 組合框)信息,不能識別自定義控件[4-5]。因此,在有自定義控件的軟件中,自動化測試用例功能點覆蓋率比較低,無法使用通用自動化測試工具實現(xiàn)完全自動化。
本文提出一種新的測試工具 GAT(Graphic Automatic Tester)設計方法和技術,應用于Windows下被測程序自定義控件比較多,對GUI自動化測試覆蓋率要求比較高的測試場景。插件方法可以讓程序的設計和開發(fā)人員為測試工具的完善提供支持,測試工具能夠識別所有的自定義控件,提供的測試腳本語言能夠描述所有的自動化測試用例。采用關鍵字驅動技術,測試人員編輯測試腳本更加簡單易懂,能大大提高整個自動化測試過程的效率。
DLL(Dynamic Link Library)文件是 Windows操作系統(tǒng)中動態(tài)鏈接庫文件,只有當EXE程序確實要調用這些DLL模塊的情況下,系統(tǒng)才會將它們裝載到內存空間中。這種方式不僅減少了EXE文件的大小和對內存空間的需求,而且使這些DLL模塊可以同時被多個應用程序使用?!癉LL注入”是指將一個DLL加載到一個正在運行的進程中,以達到執(zhí)行DLL中的代碼的方法[6]。
DLL注入的基本思路是要求目標進程中的線程調用 LoadLibrary函數(shù)來加載目標 DLL。為了防止一個進程破壞另一個進程的運行,Windows的大多數(shù)函數(shù)允許進程只對自己進行操作,有些函數(shù)卻允許一個進程對另一個進程進行操作,比如 Create-RemoteThread,這是一個為調試程序設計的函數(shù),卻使我們能夠在另一個進程中創(chuàng)建線程[7]。
遠程DLL線程DLL注入一般來說可以分為 4個步驟:
(1)VirtualAllocEx函數(shù),分配遠程進程的地址空間中的內存。
(2)再使用WriteProcessMemory函數(shù),將DLL的路徑名拷貝到第一個步驟中已經(jīng)分配的內存中。
(3)使用GetProcAddress函數(shù),獲取LoadLibrary(在Kernel32.dll中)函數(shù)地址。
(4)使用CreateRemoteThread函數(shù),在遠程進程中創(chuàng)建一個線程,它調用正確的 LoadLibrary函數(shù),為它傳遞第一個步驟中分配的內存的地址。
COM(Component Object Model)是由 Microsoft提出的組件標準,它具有語言無關性和進程透明性的特點。COM規(guī)范的定義不依賴于特定的語言,因此,編寫組件對象所使用的語言與編寫客戶使用的語言不同,只要他們都能夠生成符合 COM 規(guī)范的可執(zhí)行代碼即可。在客戶/服務器模型的軟件結構中,運行在客戶端的代碼和運行在服務器端的代碼,既可以在同一進程中,也可以在不同進程中。COM的語言無關性,使得 COM 接口函數(shù)可以被任何腳本語言所調用,具有廣泛適用性。測試工具可以選擇最合適的腳本語言調用COM接口。
COM組件有 IUnknown、IClassFactory、Idispatch三個最基本的接口類[8],分別是用于生存期控制、接口查詢,創(chuàng)建 COM 組件和調度功能。IDispatch接口為自動化接口,有了IDispatch接口,腳本語言象VBScript、JavaScript等就能使用COM組件了。實現(xiàn)了 IDispatch接口的組件,其實就是自動化組件。IDispatch接口有4個函數(shù),GetTypeInfoCount,GetTypeInfo,GetIDsOfNames,Invoke,Python 等解釋語言的執(zhí)行器就通過這僅有的4個函數(shù)來執(zhí)行組件所提供的功能。
為了便于系統(tǒng)的開發(fā)和維護,GAT采用分層設計,由上至下分為應用層,抽象層,功能層和插件層。應用層負責測試業(yè)務功能,保證測試用例的執(zhí)行,主要包括測試腳本和測試數(shù)據(jù)兩部分內容。抽象層按照被測試程序控件對象對底層函數(shù)進行了封裝,屏蔽底層功能實現(xiàn)細節(jié)。功能層負責將DLL插件遠程注入被測試程序,模擬用戶向被測試程序發(fā)送鼠標和鍵盤消息。插件層主要負責被測試程序控件信息的獲取[9-12]。
GAT啟動,GetTestInfo插件被映射到被測程序空間后,收集被測程序相關信息。對象庫中控件類(Button,)進行初始化,通過 PT代理調用GetTestInfo的接口函數(shù),獲得辨別控件對象句柄和取得對象的坐標位置等信息,并把信息賦值給對象庫中對應類成員。測試腳本執(zhí)行測試用例,按照要求在指定位置或者控件句柄發(fā)送鍵盤或者鼠標事件,完成用戶單個行為模擬。圖1展示了GAT的架構圖。
應用層主要包括了測試腳本和測試數(shù)據(jù)的組織和執(zhí)行。為了便于共享測試數(shù)據(jù),GAT將相關聯(lián)數(shù)據(jù)統(tǒng)一管理起來作為測試數(shù)據(jù)文件,如被測程序需要用到的帳號數(shù)據(jù)另外放在測試數(shù)據(jù)文件中。測試用例表示復雜的場景時,GAT將其分割為多個測試用例并將這些測試用例分組到一個測試套件中,然后使測試腳本與該套件中的每個測試用例相關聯(lián)。借助測試套件,對構成測試套件所測試的復雜場景的相關測試用例的執(zhí)行進行規(guī)劃、啟動和跟蹤。整個測試腳本由測試用例腳本和測試套腳本組成,一般測試用例腳本執(zhí)行具體的測試用例操作,測試套腳本主要用于執(zhí)行整個測試場景的一些初始化或結束被測程序的操作,使用Python語言編寫。
圖1 GA T總體架構圖Fig.1 GA T architecture
抽象層封裝了GetTestInfo插件和GUI測試庫的底層函數(shù),實現(xiàn)了簡化測試腳本編寫的功能。抽象層將被測試程序界面中存在的所有對象實體一一映射成邏輯對象,如對話框,編輯框,按鈕等控件分別與抽象層中Dialog,Edit,Button等Python類相對應,測試腳本針對邏輯對象進行。抽象層降低了應用層測試腳本與功能層 GUI測試庫接口的耦合性,測試腳本編寫更直觀。被測程序界面改變時,抽象層的存在可以大大減少測試維護的工作量,寫腳本也更容易。有了抽象層,測試腳本不再需要關心底層的函數(shù),因為抽象層中的邏輯對象會去調用底層提供的函數(shù)接口實現(xiàn)。
功能層包括了GUI測試庫和PT代理。用戶對GUI程序的操作可以分為有限幾種類型,比如單雙擊鼠標、鍵盤輸入字符等,因此在GAT框架中編寫了一個GUI測試庫來支持函數(shù)的復用,減少上層調用函數(shù)的代碼量。與用戶GUI程序操作相對應,它把 Windows操作系統(tǒng)的 API封裝成通用的Mouse_RightClick等函數(shù)庫。PT代理初始化時,將GetTestInfo.dll注入被測試程序,保存COM接口指針。運行過程中,PT將GetTestInfo.dll獲取的窗口文件句柄和空間坐標位置信息實時傳遞給抽象層。
插件主要實現(xiàn)注入和卸載過程、跨進程通信、獲取被測試程序控件信息功能。進行GetTestInfo.dll動態(tài)庫注入時,PT代理先把DLL映射到自己的進程空間,再通過調用 CreateRemoteThread實現(xiàn)了DLL注入與卸載,為了保證遠程創(chuàng)建線程調用成功,PT通常還需要調用等待函數(shù) WaitForSingleObject與被測進程同步。
測試用例腳本由腳本解釋器解釋運行,它運行在腳本解釋器的進程空間中,而GetTestInfo.dll在被測程序的進程空間運行,因此要解決跨進程通訊問題。ATL是Microsoft Visual Studio提供的一套基于模板的 C++類庫[13],利用模板類快速建立的 COM組件程序。采用 COM 接口的代理和存根模式,使得跨進程間通信就像“指針”調用,而不用考慮跨進程細節(jié)問題。GetTestInfo.dll生成COM對象實例Cget::CreateInstance(&g_pGet),CreateInstance 是CGet類的成員函數(shù),它創(chuàng)建一個CGet實例,然后調用PT代理的COM服務把創(chuàng)建實例的指針傳遞給PT代理。CGet類的定義如下。
IGet是 COM接口類。接口類里的成員函數(shù)都是虛函數(shù),CGet繼承 IGet類,IGet里的成員函數(shù)在CGet中定義。
測試腳本調用PT傳過來的COM接口指針獲取被測程序GUI界面的信息。GetTestInfo.dll與被測試程序位于同一進程空間中,可以直接調用目標進程中的函數(shù),從而獲取被測試程序信息。GetTestInfo.dll運行的COM實例CGet繼承IGet接口,因此把測試腳本需要用到的獲取信息封裝成 IGet的接口函數(shù)后,可以由測試腳本調用。
對象庫根據(jù)界面的層次關系用 XML的層次表現(xiàn)出來。以圖2為例,它在對象庫中的代碼表現(xiàn)形式如下。
其中,“Python 2.7.13 Setup”對話框是一個父窗口,其它控件都在這個對話框上面,所以在對象庫中,這個對話框作為最外層的一個 XML節(jié)點。它屬于Dialog類,所以它的class是”Dialog”類型。其它的Back按鈕,Next按鈕,Cancel按鈕等控件都在對話框上面,所以它們是“Python 2.7.13 Setup”對話框節(jié)點的子節(jié)點。這樣就形成了一個對象庫。
圖2 界面示例Fig.2 GUI demo
對對象庫的解析過程就是Python對XML的解析過程。Python中的XML庫支持解析XML[14],所以Python在解析XML前需要import xml,然后通過XML庫函數(shù)實現(xiàn)對XML的解析。代碼如下所示:
pathList = UniStr(self._path).split('-')
for i in range(len(pathList)):
if i == len(pathList)-1:self._obj=objList.GetObjectByText(objTxt, self._class)
else: self._obj = objList.GetObjectByText(objTxt)
if self._obj.HasAttr('WCtrl'):
解析模塊首先會把對象的路徑通過“-”把界面的層次間隔開來。抽象層解釋測試腳本時,首先把路徑通過“-”分隔開,然后按照XML的相應的層次關系,在 XML中一步一步深入到內層節(jié)點,找到目標節(jié)點,完成了對象庫的解析過程。路徑表示窗口的一個父子關系,父窗口寫在前面,它的子窗口寫在后面。
為測試腳本采用了關鍵字腳本技術[15],測試腳本中的對象關鍵字Dialog等都由抽象層中對應的類解釋。抽象層解釋類時,首先做類的初始化,通過對象庫唯一標識對象。當類初始化完畢,對象所有的屬性,如坐標位置等,都存儲在類的成員變量中。測試腳本中的對象行為由類的成員函數(shù)完成。測試腳本如下所示:
Dialog('主面板').RClickInDialog(a[6]+5,a[7]+5)Sleep(1)
Menu('設置').Select('我的信息')
Sleep(1)
Edit('搜索-條件匹配').SetValue(ATSP['UID3']['account'])
Button('主面板-打開管理器').Click()
腳本的最后一句中,“主面板-打開管理器”表示父窗口是“主面板”,“打開消息管理器”是“主面板”的一個子窗口。這句話的意思是單擊“主面板”里的“打開管理器”的按鈕。測試腳本執(zhí)行時,抽象層首先會調用 Button的__init__成員函數(shù)對Button進行初始化。初始化函數(shù)會調用_findObj函數(shù),這個函數(shù)通過對象庫對“主面板-打開管理器”進行解析。當抽象層執(zhí)行Button的Click時,Click成員函數(shù)會去調用GUI函數(shù)庫里的MouseClickInsideHWnd,然后操作系統(tǒng)會在Button的坐標處產生鼠標點擊這一虛擬事件。
本文針對GUI測試的獨特性和當前常用測試工具的一些缺點,對GUI自動化測試策略,方法和技術進行了深入的探討。提出了新的設計,采用插件技術,關鍵字驅動測試腳本,對象庫,GUI測試庫。
使得在Windows下,測試工具的功能得到了很大的增強,腳本的編寫也變得簡單,編寫效率也有了大大的提高。GAT全部采用腳本編寫模式測試,完全了擺脫了傳統(tǒng)捕捉/回放模式,自動化覆蓋達到90%以上,而且自動化測試工具運行也變得更加穩(wěn)定。目前 GAT在 GUI自動化測試解決方案的實現(xiàn)過程中,只是形成了一個具有簡單功能的GUI自動化測試工具。實現(xiàn)屏幕錄制,支持聯(lián)機和單機工作模式,支持數(shù)據(jù)庫和文件存儲模式,支持多機協(xié)作將是GAT下一步研究工作的重點。
[1] 鄭翠. 基于SilkTest工具對山東移動iCRM系統(tǒng)的自動化測試[D]. 濟南: 山東大學.
[2] 王沖. Java GUI自動化測試工具的實現(xiàn)[D]. 上海: 東華大學.
[3] 張競帆. 基于Selenium的一種Web自動化測試系統(tǒng)的設計與實現(xiàn)[D]. 北京: 北京交通大學.
[4] 吳瓊. 基于QTP自動化測試框架的研究與應用[D]. 北京:中國科學院大學.
[5] 王健, 李亞偉, 朱璇. 使用QTP 對Silverlight 應用進行自動化測試的研究與實踐[J]. 軟件, 2014. 35(4): 18-20.
[6] 陳莊, 王津梁, 張醍. 手工DLL注入的檢測方法研究與實現(xiàn)[J]. 信息安全研究. 2017, 3(3): 246-253.
[7] Jeffrey Richter. Windows核心編程(第5版)[M]. 葛子昂等譯.北京: 清華大學出版社, 2008.
[8] 何卉, 徐志躍, 張秀磊. 基于COM 組件技術的測控系統(tǒng)軟件框架設計[J]. 電子設計工程. 2017, 25(11): 61-64.
[9] 吳立金, 韓新宇, 張凱等. 一種非侵入的GUI自動化測試系統(tǒng)設計[J]. 計算機測量與控制.2017, 25(12): 49-53.
[10] 涂華軻, 鄒華, 林榮恒. 增強的云化并行計算框架系統(tǒng)的設計與實現(xiàn)[J]. 新型工業(yè)化, 2012, 2(12): 33-40.
[11] 王影, 劉卉, 趙娟. 軟件部件仿真測試平臺的設計與實現(xiàn)[J]. 計算機工程與設計. 2017, 31(11): 3061-3065.
[12] 王如迅. 基于SWTBot技術的軟件自動化測試的研究與實現(xiàn)[J]. 軟件, 2016, 37(02): 121-128
[13] Brent Rector, Chris Sells.ATL技術內幕[M]. 北京: 科學出版社, 2003.
[14] Wesley Chun. Python核心編程(第3版)[M]. 孫波翔等譯.北京: 人民郵電出版社, 2016.
[15] 黨若楠, 陳健. 基于關鍵字驅動的Web自動化測試框架研究與實現(xiàn)[J]. 工業(yè)控制計算機.2017, 30(9): 46-47.