加入RUN!PC粉絲團
最近新增的精選文章
 
最多人點閱的精選文章
 
 
精選文章 - 開發技術
分享到Plurk
分享到FaceBook
 
自動程式碼UI測試(下)
文‧圖/胡百敬 2012/10/11 下午 05:57:45

在上期專欄中,我們已經介紹如何透過Visual Studio 2012 Premium以上的版本建置基本的自動程式碼UI測試。在本期專欄中,將繼續說明如何進一步操控自動程式碼UI測試。
資料繫結
當我們執行測試時,多需要透過資料驅動以因應不同需求。在Visual Studio 2010時可以直接透過精靈介面完成,可惜的是Visual Studio 2012反而無法做到。若要將自動程式碼UI測試設定成資料驅動測試,這與設定「單元測試」的方式相同,需要手動為測試方法添加屬性。

在此簡單示範透過資料驅動,測試小算盤時可以依據資料內容點選不同的按鈕,並驗證不同的結果。首先以XML格式定義資料內容,並存放在Visual Studio專案下,讓測試資料隨著專案走。如表1所示:

▲ 表1:以多個元素和屬性代表執行數次的多筆紀錄



表1的XML內容是為了對應小算盤的測試輸入,在此以逗號分格需要點選的按鈕,並故意分成運算元(Operand)、運算子(Operator)1,和按完按鍵後小算盤應呈現的結果(Result),以驗證測試是否正確。
由於在測試時,該XML檔案需要在專案建置後能夠與DLL檔一併輸出到相同的目錄下,因此在「方案總管」視窗點選該XML檔案,並在「屬性」視窗的「複製到輸出目錄」屬性選擇「永遠複製」選項。如圖1所示:


▲ 圖1 設定專案建置時,要將測試資料複製到目的目錄


當然,你也可以逗號分隔檔、資料庫內的資料表定義測試資料。只要在測試方法賦予「DataSource」屬性時,告知資料源的提供者為何。此處用的是:

Microsoft.VisualStudio.TestTools.DataSource.XML

若你有一個固定的測試環境,可以考慮用資料表當作繫結的資料源。但若測試可能部署到不同環境,而測試資料需要隨著測試一起部署,則可能XML或逗號分格檔較為方便。

設定測試方法的DataSource屬性可以參照線上說明:

http://msdn.microsoft.com/zh-tw/library/microsoft.visualstudio.testtools.unittesting.datasourceattribute(VS.90).aspx

整個資料繫結的測試方法如範例程式碼1所示:

範例程式碼1:透過「DataSource」屬性指定資料繫結的資料源,經由「TestContext」物件讀回測試資料


在範例程式碼1中,透過Visual Studio預設的測試範本自動帶入的「TestContext」類別,可以讀取其「DataRow」集合,傳回資料繫結所設定資料源的欄位,讀取該欄位的值且賦予到要點選的小算盤按鍵:

ClickButton(TestContext.DataRow["Operand1"].ToString());

一般而言,若要修改針對待測程式輸入的參數,可改寫「自動程式碼UI測試產生器」自動建立的「<錄製的函數名稱>Params」類別內之各項屬性,在本範例中,你可在UIMap.Designer.cs檔案搜尋LaunchCalcParams類別,其內定義著「小算盤」應用程式存放在作業系統上的檔案路徑。

在實際執行該測試方法時,可以「<錄製的函數名稱>Params」類別的屬性值來賦予測試步驟所需的輸入值,其後自動化地重複以不同的紀錄執行測試。例如:

this.UIMap.LaunchCalcParams.UI小算盤WindowExePath = TestContext.DataRow["欄位"].ToString()

由於這次操作測試時,並沒有參數輸入的部分,因此直接透過資料繫結的值找尋物件,再用滑鼠點選該物件。其找尋物件並以滑鼠點選的程式碼如範例程式碼2:

範例程式碼2:物件的SearchProperties找尋物件,並以Mouse.Click模擬滑鼠點擊該物件


設定完資料繫結之後,測試代理程式執行「自動程式碼UI測試」時,每次從資料來源讀到一筆記錄(在此是指XML檔案內每一個Row元素),便會呼叫myDatabind方法一次,方法內的測試流程就執行一遍。以表1的測試資料為例,myDatabind方法會執行三次。透過「測試總管」視窗點選該測試時,從下方的結果視窗可觀察使用各筆記錄執行測試的結果:


▲ 圖2 透過「測試總管」觀察資料繫結測試的執行結果


使用Using
當以測試代理程式執行測試方法,呼叫另外一個待測程式以執行測試邏輯時,要特別注意所耗用的系統資源。若重複地載入程式而因故沒有釋放,可能造成系統資源用盡。儘量使用using語法是釋放資源的解法之一,如範例程式碼3所示:

範例程式碼3:以using保障釋放資源


由於.NET Framework提供之掌控待測程式的ApplicationUnderTest類別有實做IDisposable介面,若以using區塊包裝使用到待測程式的程式碼,則因任何原因離開該區塊時,都可以自動呼叫Dispose方法而釋放資源。
範例程式碼3中,我們故意觸發例外,而在catch區塊呈現對話窗,你可以嘗試註解掉以下程式碼與相關的大括號:

using (ApplicationUnderTest uI小算盤Window =
ApplicationUnderTest.Launch(this.UIMap.LaunchCalcParams.UI小算盤WindowExePath,
this.UIMap.LaunchCalcParams.UI小算盤WindowAlternateExePath))

並回復原先載入待測程式的方式:

this.UIMap.LaunchCalc()

則發生例外狀況後,在對話窗持續存在時,待測程式也會持續存在而消耗資源。反之,若以範例程式碼3的寫法,程式一旦執行到catch區塊,離開了using區塊,也就自動關閉了待測程式。所以只剩對話窗而沒有小算盤。

截圖
若操作待測程式執行時,需要抓取當時的畫面,以確認版面正確與否,可透過掌握待測程式的物件;從.NET Framework提供的繼承自UITestControl類別2之CaptureImage方法取得畫面,如範例程式碼4所示:

範例程式碼4:以CaptureImage取得視窗畫面,並透過TestContext物件的AddResultFile方法將圖檔加入到測試結果輸出目錄


範例程式碼4也透過DataSource屬性,讓測試方法資料繫結先前表1定義的XML內容,則該方法被重複執行三次。透過CaptureImage方法取得3個執行畫面,分別存檔後,再以TestContext物件的AddResultFile方法將這些圖檔放入到測試結果中3,因此如圖3所示,可以在測試結果可以看到測試輸出中,還有自動編號的3個圖檔:


▲ 圖3 透過「測試總管」檢視截取的應用程式畫面


自行呼叫自動程式碼UI測試架構
若不想透過「自動程式碼UI測試產生器」錄製並建立多個程式類別,僅想自己撰寫程式碼呼叫待測程式,則可以在建立自動程式碼UI測試專案後,於程式碼檔案開始處加入對以下命名空間的參照:

using Microsoft.VisualStudio.TestTools.UITesting.WinControls;

而後可以範例程式碼3所示範的ApplicationUnderTest類別之Launch方法啟動待測程式。在此另外示範透過WinWindow類別的Find方法,找尋在當下Windows系統上已經開啟的視窗程式。並搭配練習設定重播(Playback)引擎,以調整找尋待測程式的方式:

範例程式碼5:自行透過.NET Framework提供的自動程式碼UI測試之相關類別,直接操控待測程式



在執行上述測試之前,需要先開啟Windows作業系統提供的「記事本」工具程式。而後再執行測試。按照範例程式碼5的邏輯,會先找尋Windows系統正在運行視窗程式,其名稱含有「未命名」字樣,這不一定會是剛開啟的記事本,端看是否有同名或近似的其他程式,以及搜尋的順序。

找到名稱有「未命名」字樣的程式碼,會先以粗線條外框標示,接著輸入以下字串:

Hello Coded UI Test

最後,關閉應用程式並不要儲存,其執行流程如圖4所示:


▲ 圖4 以程式找尋已開啟的記事本,並輸入內容和設定存檔與否


在此,我們故意讓輸入法是採用中文「新注音」輸入,導致重播輸入失敗,但我們要忽略這個失敗,則需要設定Playback 類別的 PlaybackSettings之ContinueOnError屬性,讓錯誤發生後仍正常執行。
透過設定Playback類別的PlaybackSettings提供之多種屬性,在重播自動程式碼UI測試時,可以改變自動程式碼UI測試的「記錄和重播引擎(Record and Playback Engine)」執行方式。以下簡單列舉數個屬性說明。

發生錯誤後繼續(ContinueOnError)
例如,當錄製網頁的登入頁面時,IE瀏覽器可能彈出「自動完成密碼」對話框,但當重播測試登入頁面時,IE瀏覽器將不再詢問這個問題,因此,重播測試會因找不到物件而錯誤。為了提高測試應變能力,可透過如下設定:

Playback.PlaybackSettings.ContinueOnError = True

完成上述設定之後,播放測試時若發生錯誤,測試引擎將繼續執行下一個動作。在範例程式碼5簡單模擬這個問題,只要叫起記事本程式碼後,切換輸入法至中文即可導致無法輸入原本程式要輸入的英文。此類部分地方可能有潛在但不重要的錯誤,透過 ContinueOnError 屬性跳過:

Playback.PlaybackSettings.ContinueOnError = true;

//若輸入法是中文,會出錯
we.Text = "Hello Coded UI Test";
Playback.PlaybackSettings.ContinueOnError = false;


但記住把ContinueOnError 屬性設為true之後,在跳過有潛在錯誤的可能、但錯誤不重要的程式區段之後,要還原回false,否則測試引擎將忽略其後所有的錯誤。預設該屬性為false。
此外,若以「自動程式碼UI測試產生器」工具程式錄製測試步驟,在執行個別動作時,設定發生錯誤後是否要繼續,可以透過「自動程式碼UI測試編輯器」設定,如圖5所示:


▲ 圖5 針對錄製的測試步驟其內各動作,可個別設定發生錯誤後是否要繼續


如圖5,在左方「UI動作」樹狀結構展開否個方法後,點選其內的動作(此處為「按一下’+’按鈕」),而後在「屬性」視窗可以點選「錯誤時繼續」屬性,設定該動作發生錯誤後,是否要繼續。

快速失敗(ShouldSearchFailFast)
記錄和重播引擎的搜索演算法有一個快速失效的邏輯,即引擎如果判讀搜尋不會成功,就回傳錯誤。這可確保等待搜尋結果不需2分鐘(預設的搜尋逾時等待時間)。
若控制項需等待一段時間才會變為可用,可能這段時間也超過搜尋逾時設定,你可以設定ShouldSearchFailFast屬性停止快速失敗,並一起調高搜索逾時設定值,讓記錄和重播引擎等待該控制項:
Playback.PlaybackSettings.ShouldSearchFailFast = False
在範例程式碼5中我們是啟動搜尋快速失效,並降低逾時門檻,以加快放棄尋找已經在執行的應用程式,其視窗是否有「未命名」字樣。

智慧比對(SmartMatchOptions)
記錄和重播引擎藉由「搜索屬性」來找尋使用者介面的控制項。例如,尋找前述測試的視窗上的按鈕控制項,其在UIMap.Designer檔案預設產生的程式碼如下:

WinButton btn = new WinButton(w);
btn.SearchProperties[WinButton.PropertyNames.Name] = "關閉";


一些用於搜索的屬性定義可能會隨著時間而改變。記錄和重播引擎則會利用「智慧比對演算法(Smart Match algorithm)」辨識控制項,如果無法使用完全相同的屬性找到所需的控制項,智慧比對演算法會嘗試搜索控制項上不同的屬性。當然,你可以自行修改程式。若以「錄製/重播」執行測試時找不到物件,也可以嘗試修改「自動程式碼UI測試產生器」工具所產生的程式碼,但不建議如此,因為那很容易被重新產生的程式碼取代掉。

在預設情況下,智慧比對會用在外層視窗及所有的控制項。若要關閉智慧比對,可以使用設定值SmartMatchOptions.None,範例如下:

Playback.PlaybackSettings.SmartMatchOptions = SmartMatchOptions.None

預設值為「TopLevelWindow」,也就是自動智慧比對最上層視窗,這是較彈性的使用者介面搜尋, 但有時可能會導致搜尋錯誤。

精確匹配階層(MatchExactHierarchy)
記錄和重播引擎使用階層結構來定位各個目標控制項。例如,按下詢問對話框的「確定」按鈕,該按鈕實際是在如圖6的階層架構之中:


▲ 圖6 記錄和重播引擎使用階層結構來定位各個目標控制項


在重播時要找到「UI不要儲存NButton」,引擎會先搜索「UI記事本Window」,然後尋找其內的「UI記事本Pane」,最終在「UI記事本Pane」內找「UI不要儲存NButton」。應用程式可能會改變物件的階層結構,此時雖然階層結構不同,記錄和重播引擎仍會試圖尋找控制項,自動跳過中間的物件階層。在這個範例中,如果它不能找到「YesWindow」,便會搜尋直屬「UI記事本Window」的「UI不要儲存NButton」及其所有子物件。若不想如此,可以透過「MatchExactHierarchy」屬性要求只找尋符合階層的物件:

Playback.PlaybackSettings.MatchExactHierarchy = True

如果該屬性設為True,則僅依階層搜尋控制項,任何失敗都將導致無法找到控制項。預設該屬性設為False。請注意,這有時會導致找錯控制項,而不得不更改此設定。

在最小化的視窗搜尋(SearchInMinimizedWindows)
即使受測的視窗已經被最小化,記錄和重播引擎仍會將最小化的視窗恢復,並在其內搜尋與操作控制項。你可以在測試方法內,於叫起待測程式的方法之後,加上以下暫停測試流程3秒的程式碼:

Playback.Wait(3000);

利用這3秒,將被測試引擎叫起的待測應用程式之視窗最小化,3秒後,將會發現該視窗被還原並繼續執行測試。

測試代理程式執行記錄
我們所撰寫的測試程式最終是編譯成DLL,由測試代理程式(QTAgent32.exe)載入執行。若要檢視測試代理程式的執行記錄,以找尋執行測試可能的問題,可以檢視以下目錄內:
<系統安裝磁碟>:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE
QTAgent32自動產生的檔案:

UITestPlaybackResults_QTAgent32.htm

其內容約略如圖7所示:


▲ 圖7 測試代理程式的執行記錄


若沒有該檔案,可以嘗試設定QTAgent32.exe的config檔案,以啟動記錄:

<系統安裝磁碟>:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\QTAgent32.exe.config



參考資料
自行撰寫自動程式碼UI測試:
http://www.cnblogs.com/hwade/archive/2010/05/19/1738801.html

如何讓自行開發的Windows控制項可以被自動程式碼UI測試呼叫執行(Coded UI Test Extension for 3rd party Windows Forms controls–How to?):
http://blogs.msdn.com/b/visualstudioalm/archive/2011/10/28/coded-ui-test-extension-for-3rd-party-windows-forms-controls-how-to.aspx

1 就這個測試示範而言,指定按鍵的部分當然可以不分運算元和運算子,重頭到尾用逗號分格標示所有要點選的按鍵之Name屬性即可,但為了示範,我們故意讓XML有多一點的屬性。
2 在此範例中代表小畫家畫面的「UI小算盤Window」物件類別繼承自.NET Framework提供的WinWindow類別,而WinWindow類別繼承自WinControl類別,WinControl類別繼承自UITestControl類別,UITestControl類別內有定義CaptureImage方法。
3 預設在與測試專案相同方案的目錄下,會另外建立一個TestResults子目錄。每次執行完測試,都另在該目錄下建立存放測試執行狀況的子目錄,其目錄名稱帶有時間資訊。再於其內建立名稱為In/Out的子目錄,於In子目錄下另有以UUID為名稱的子目錄,其內擺放這些圖檔。