https://segmentfault.com/a/1190000010339021
從事Windows 桌面應用自動化測試也有一些年了,現在談這個話題並不流行。因為除了企業級應用,很少有公司會只選擇Windows桌面作為目標用戶平台,一般都會考慮跨平台的瀏覽器解決方案,桌面應用的地位漸漸下降,這是事實。
當年初入測試行業時就被外包公司看上了,在微軟的圈子里一待就是4年,時間真快。不得不說,一個大學剛畢業的毛頭小子看到微軟里各種技術和工具真像極了劉姥姥進大觀園,那時候還沒有iPhone,也沒有Android,微軟一統天下。
本文主要介紹一下我對Windows UI自動化的一些看法以及WPATH的實現和應用,如果你還在從事Windows桌面應用的自動化測試,應該能有一些幫助。
為何發明WPATH
Windows UI 自動化,顧名思義就是在Windows平台實現軟件的界面自動化,比如自動打開Excel填入一些數據,輸入公式,獲取結果。正經的用途就是軟件自動化測試,避免重復的手工操作;不正經的用途就是寫外掛,各種投機取巧的工具等等。
最簡單粗暴的實現方案就是錄制回放,優點很明顯,簡單快速;缺點也一樣明顯,不可靠因素太多。主要的代表就是QTP,來自HP公司,這應該是很多同學都聽過的一款測試工具。
進階的方案就是使用微軟提供的自動化工具集:UI Automation。UI Automation是Microsoft .NET 3.0框架下提供的一種用於自動化測試的技術,是在MSAA基礎上建立的,MSAA就是Microsoft Active Accessibility。
如果你使用過.NET 提供的UI Automation相關的類庫,應該有一個直觀的感受,就是非常啰嗦,舉一個例子:
AutomationElement ControlTypeComboBox = grdClassBook.FindFirst(
TreeScope.Children,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.ComboBox)); AutomationElement cellElement = ControlTypeComboBox.FindFirst( TreeScope.Children, new PropertyCondition(AutomationElement.AutomationIdProperty, "ListBox"));
每當你嘗試去獲取一個UI元素時,都需要使用FindFirst
之類的方法去查詢指定的PropertyCondition
,而PropertyCondition
使用起來也不簡單,特別是當你需要拼接多個AND或者OR多個條件時。
var btnCondition = new AndCondition( new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button), new PropertyCondition(AutomationElement.NameProperty, "ok"));
才兩個條件就這么多代碼了?你看看搞Web自動化的同學都可以用XPATH,快速定位和查詢元素 /div[@id='ok']
,多好。既然我們那么羡慕XPATH,那我們就搞一個出來,讓做Windows桌面自動化的同學也可以High一把。
WPATH實現原理
具體代碼我就不在此贅述了,想讀代碼的同學可以直接移步至Github:https://github.com/tobyqin/wpath。
WPATH的主要原理就是通過反射的方式去獲取當前方法或者屬性的Attribute,在Attribute中我們可以定義類似於XPATH的語法,我 且稱之為WPATH。最后經過表達式解析轉換成對應的Find方法和Condition,舉一個例子說明:
[WPath("/Edit[@id='txtId' or @Class='TextBox']")] public AutomationElement EditControl { get { return this.AppElement.FindByWPath(); } }
當調用FindByWPath()
時,該屬性上的WPath Attribute就會被解析出來,其中的 /
會被解析成FindFirst,Edit
會被解析成ControlType.Edit,中括號里的條件最后被組合起來,調用的最終結果大致如下:
public AutomationElement EditControl { get { return this.AppElement.FindFirst(TreeScope.Children, new AndCondition( new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit), new OrCondition( new PropertyCondition(AutomationElement.AutomationId, "txtId"), new PropertyCondition(AutomationElement.Class, "TextBox")))); } }
痛苦的感覺一下減輕許多,有沒有?
更詳細的WPATH用法
如果你要在項目中使用WPATH,可以通過nuget包安裝:
PM> Install-Package WPath
簡單說明
-
WPath 和 XPath 類似,以 '/' 開頭。
-
可以使用多個 '/' 來定位目標元素。
-
節點名字來自於MSDN定義好的 control type。
-
目前WPath支持的查詢屬性如下:
-
Name
(NameProperty) -
ID
(AutomationIdProperty) -
Class
(ClassNameProperty) -
Enabled
(IsEnabledProperty) -
FrameworkID
(FrameworkIdProperty)
舉例子
/Group/Button
-
獲取第一個Group下的第一個Button。
//Button[@Name='Save']
-
在子孫節點中獲取第一個Name為 "Save" 的元素。
/[@Name='TabContainer']/Button[2]
-
獲取Name為 "TabContainer"的控件下的第二個Button,注意,控件類型名稱可以為空。
/Button[@ID='AddButton' and @Name='Add']
-
獲取一個automation ID 為 'AddButton' 且 name 為 'Add' 的Button。
/Button[@ID='AddButton' or @Name='Add']
-
獲取一個automation ID 為 'AddButton' 或 name 為'Add'的Button。
/Button[first()]
-
獲取當前元素下第一個Button。
/Button[last()]
-
獲取當前元素下最后一個Button。
實際運用
推薦使用Attribute的方式進行調用,可用於類方法或者屬性。
[WPath("/Edit[@id='txtId' or @Class='TextBox']")] public AutomationElement EditControl { get { return this.AppElement.FindByWPath(); } } [WPath("/Button[first()]")] public AutomationElement GetFirstButton() { return this.AppElement.FindByWPath(); }
或者直接調用 FindByWPath(path)
來定位目標元素。
var path = "/Edit[3]"; var e = this.AppElement.FindByWPath(path); Assert.AreEqual("txtKey", e.Current.AutomationId); Assert.AreEqual(ControlType.Edit, e.Current.ControlType); path = "/Button[@name='OK']/Text[1]"; e = this.AppElement.FindByWPath(path); Assert.AreEqual("OK", e.Current.Name); Assert.AreEqual(ControlType.Text, e.Current.ControlType);
小貼士
-
元素類型節點是大小寫不敏感的,比如:
-
@name = @Name
-
/edit = /Edit
-
-
父節點定位
../
目前不支持,因為有點復雜。
更多的說明建議還是去看Github中的說明文檔,或者直接看單元測試。
后記
Windows UI 自動化的坑還是挺深的,填坑的人也不少,我推薦有需要的同學去學習和了解一下 White。White是一個非常好UI Automation 封裝框架,相信我,能省下你不少時間。