UI自動化測試中選擇頁面元素
這里討論的問題,主要是基于
HTML
的Cucumber + Puppeteer
技術棧的UI自動化測試(以下簡稱UI自動化)中遇到的問題蒜鸡。
UI自動化的一線困境
UI自動化測試在前端的開發(fā)測試中蜓洪,是一件政治正確的事情纤勒。
作為一名前端工程師(FE),很少有人敢于說出自己的心聲隆檀,UI自動化沒有什么用處摇天。但大家的身體都很誠實,或者心照不宣的在實際開發(fā)中不考慮UI自動化恐仑,或者DEMO一下點到為止泉坐,或者轟轟烈烈地搞一場虎頭蛇尾的UI自動化運動。
實踐出真知裳仆,一線開發(fā)者的真實體驗都是用真金白銀換來的腕让,需要得到尊重。不過歧斟,尊重常識也很重要纯丸。高速機槍的殺傷效率一定高于拉栓的步槍,自動化生產(chǎn)線的生產(chǎn)效率一定高于手工作坊静袖。為什么UI自動化測試會比人工的UI測試效率低呢觉鼻?
政治正確也沒毛病,我們需要確立一個基本信念队橙,自動化的效率一定高于人工坠陈,而且是數(shù)量級壓制的差異。
在一線實踐中捐康,發(fā)現(xiàn)UI自動化效率不能盡如人意的時候仇矾,千萬不要輕言放棄。要認真研究一下解总,我們的使用方法是不是科學若未,成本結構的瓶頸在什么地方?
總結UI自動化實踐的制約因素倾鲫,會發(fā)現(xiàn)不管是在用法上,還是在成本結構上萍嬉,選擇頁面元素都是一處很重要的瓶頸乌昔。
選擇頁面元素的問題
從UI自動化最小DEMO演示(打開 1 個頁面,選擇 1 個按鈕壤追,點擊 1 下)磕道,到全流程、全要素UI自動化回歸測試行冰,選擇 HTML
頁面上的元素都是基礎中的基礎溺蕉。劇本(Feature
)和場景(scenario
)從簡單到復雜伶丐,選擇元素的重要性呈線性增長。如果選擇的效率低疯特,場景劇本在開發(fā)調試上耗時長哗魂、成本高,日常維護的返工概率高漓雅、頻率高录别、幅度大。在規(guī)模較大的復雜UI自動化中邻吞,選擇元素的效率有一點微小變化组题,都會給總體成本帶來巨大影響,甚至成為制約UI自動化規(guī)模的天花板抱冷。
在傳統(tǒng)前端的 jQuery
時代崔列,使用 $
函數(shù)選擇頁面元素,基本上是其他代碼運行的前提旺遮。在 VUE
赵讯、 React
等現(xiàn)代前端實踐中,代碼與 HTML
頁面產(chǎn)生了隔離趣效,通過引用對象或回調函數(shù)等手段與頁面元素交互瘦癌,不再需要使用 CSS
選擇器、 XPath
等手段選擇元素跷敬。從最終的 HTML
頁面上看讯私,沒有什么可以捕獲元素的把柄,弱化了UI自動化選擇元素的能力西傀。
HTML
頁面直接貼近最終用戶斤寇,是系統(tǒng)中變動頻率比較高的部分。用戶交互類的產(chǎn)品需求拥褂,又基本上都會帶來頁面變化娘锁,很容易導致日常維護中的UI自動化用例失敗。
在頁面不斷變化的背景下饺鹃,識別和提煉具有穩(wěn)定性質的選擇器(Selector)莫秆,是UI自動化成功的關鍵因素之一。
選擇器(Selector)的穩(wěn)定性質
選擇器(Selector)的穩(wěn)定性質悔详,是相對的镊屎、動態(tài)的,不總是那么顯而易見茄螃,需要認真的觀察和思考缝驳,需要精心的提煉。
HTML
頁面上有很多標簽(tag)。一般來說用狱,使用標簽(tag)做選擇器(Selector)很不穩(wěn)定运怖,不過也有例外。例如夏伊,一個管理系統(tǒng)的列表頁摇展,上面是查詢表單,下面是數(shù)據(jù)列表署海,可能整個頁面只有一個查詢按鈕吗购。或者砸狞,雖然有多個按鈕捻勉,但只有一個提交查詢請求的 submit
按鈕。那就可以把 button
或者 button[type="submit"]
作為穩(wěn)定的選擇器(Selector)使用刀森。
有的文檔結構片斷踱启,也具有穩(wěn)定的性質。例如研底,一個用戶端的頁面埠偿,分為 Header
、 Body
榜晦、 Footer
三個部分冠蒋,在 Header
中有一個搜索條件文本框和一個按鈕用來提交搜索請求。那么 header form.search input
用來選擇文本框乾胶,選擇搜索按鈕使用 header form.search button
抖剿,可能就是一個穩(wěn)定的選擇器(Selector)。
通過認真的觀察识窿,我們可以從雜亂無章的頁面中斩郎,從易變的文檔結構中,識別和提煉出具有穩(wěn)定性質的部分喻频,用來選擇元素缩宜。找到這種穩(wěn)定性質后,我們就獲得了想要抒發(fā)的詩意甥温。但要想寫出優(yōu)美的詩歌锻煌,光有詩意可不行,還需要豐富的詞匯和對詞匯的靈活運用姻蚓。從另一方面來說炼幔,詞匯掌握的好,也可以更容易的獲得詩意史简,更容易的識別和提煉頁面中具有穩(wěn)定性質的選擇器(Selector)。
選擇頁面元素的詞匯
從UI自動化的角度,我們可以把選擇頁面元素的詞匯不嚴謹?shù)臍w納為 4 類:標識類圆兵、屬性類跺讯、文檔結構類和組合應用類。
標識類
有 id
的元素殉农,是UI自動化測試最喜歡的刀脏,因為 id
屬性在設計上就是用來標識唯一元素的。
樣式類(class)在設計上超凳,本來是用來定義外觀的愈污,不應該用于選擇頁面元素。但是轮傍,如果某種外觀本身具有穩(wěn)定的性質暂雹,那就可以作為穩(wěn)定的選擇器(Selector)使用。例如创夜,在一篇文章的頁面杭跪,.title
樣式類(class)可能就具有用于選擇標題元素的穩(wěn)定性。
說句題外話驰吓,雖然 id
和 class
基本上就可以選擇到元素了涧尿,但加上元素標簽(tag)可以增加可讀性。例如檬贰,input#driverId
表示填寫司機ID的文本框姑廉,h1.title
表示文章的標題,是一種好的編碼習慣翁涤。
頁面元素的 data-testid
屬性桥言,從語義上看就是為測試服務的標識。不管是前端單元測試的遺產(chǎn)迷雪,還是專門為UI自動化提供的標識限书,data-testid
一般都具有穩(wěn)定性質。當然章咧,關于是否應該使用 data-testid
屬性的爭論倦西,則是另外一個故事。
屬性類
其實 id
赁严、 class
扰柠、 data-testid
都是屬性類的特殊形式,也可以用屬性類的詞匯來表達疼约。
在前端框架卤档、組件庫中,或者優(yōu)雅的業(yè)務代碼中程剥,前端工程師(FE)經(jīng)常會語義化的使用一些屬性劝枣。例如,antd
的 Select 選擇器有一個 role="listbox"
的屬性,就具有穩(wěn)定性質舔腾。
之所以作為一類單列出來溪胶,是因為屬性選擇器擁有類似正則表達式一樣的強大語法,在UI自動化中可以很有效的選擇頁面元素稳诚。
li[item]
可以選擇帶有 item
屬性的一組元素哗脖。在屬性選擇器前加上元素標簽(tag),也是為了增加代碼的可讀性扳还。
a[]
可以選擇跳轉到 antd
的超鏈接才避。
其他諸如元素的屬性中包含某字符串、以某字符串開頭氨距、以某字符串結尾等屬性選擇器用法桑逝,就不再一一列舉。
文檔結構類
相比于標識類和屬性類的頁面元素選擇器(Selector)衔蹲,具有穩(wěn)定性質的文檔結構就不總是那么一目了然肢娘,需要更多的觀察、識別和提煉舆驶。
類似 button
橱健,直接使用頁面元素的標簽(tag),那就是選擇所有的 <button />
元素了沙廉。
button.ant-btn
的樣式類(class)前加一個元素標簽(tag)拘荡,并不見得是為了追求代碼的可讀性,也有可能是為了和 a.ant-btn
做出區(qū)分撬陵,要選擇 <button />
而不是選擇 <a />
珊皿。
div p
表示選擇 <div>
元素內的所有 <p>
元素。只要是在 <div>
的肚子里巨税,元素 <p>
不分層級全部都會被選中蟋定。
div > p
選擇父元素是 <div>
的所有 <p>
元素。 <p>
元素不但要在 <div>
的肚子里草添,而且還要是直接子元素驶兜,層級更深一些的 <p>
就不會被選中了。
h2 + p
表示 <p>
元素是緊跟在 <h2>
元素后的第一個兄弟元素远寸。注意:緊跟著的那一個 <p>
元素抄淑。
h2 ~ p
是選擇 <h2>
元素后面的每一個 <p>
元素。
偽選擇器也可以用于對文檔結構的選擇驰后。例如肆资,:nth-child(n)
是指某父元素下的第 n 個子元素。
偽選擇器有很多灶芝,這里不一一列舉郑原。
組合應用類
五色令人目盲唉韭,五音令人耳聾。上述選擇頁面元素的 3 類詞匯犯犁,獨立使用者有之纽哥,但更多的是組合應用。
通過用心的觀察栖秕,識別和提煉出具有穩(wěn)定性質的選擇器(Selector),用簡潔的詞匯組合表達出來晓避,就可以真的為之四顧簇捍,為之躊躇滿志了。
我們以 antd
Select選擇器的基本使用 為例俏拱,演示一下頁面元素選擇詞匯的組合表達暑塑。
如上圖所示,UI自動化一個常見的場景就是點擊 antd
Select選擇器(#1)锅必,從下拉列表中選擇一個選項(#2事格,yiminghe),點擊選中搞隐。
我們右鍵點擊 #1
(1)驹愚,從右鍵菜單中選擇 檢查
(2),如下圖所示:
在 Chrome
開發(fā)者工具(F12劣纲,開發(fā)者工具 > Elements)中觀察 #1
對應的HTML代碼結構逢捺。如下圖所示:
UI自動化中點擊 #1
,實質上是選擇 input[aria-owns="rc_select_0_list"]
后點擊癞季, antd
Select選擇器(#1)的選項下拉列表就會展示出來劫瞳。
與 HTML
原生的 select
不同,選中 yiminghe
(#2)選項并不是那么容易绷柒。
還是老套路志于,在 yiminghe
(#2)選項上右鍵點擊,從右鍵菜單中選擇 檢查
废睦。在 Chrome
開發(fā)者工具(F12伺绽,開發(fā)者工具 > Elements)中觀察 yiminghe
(#2)選項對應的HTML代碼結構。如下圖所示:
選項(#2郊楣,yiminghe)的頂層容器很容易識別憔恳,就是 div.rc-virtual-list
(b)。非常遺憾净蚤,每一個 antd
Select選擇器都會渲染出一個自己的 div.rc-virtual-list
(b)钥组。
再仔細觀察,就會發(fā)現(xiàn) div.rc-virtual-list
(b)總是緊跟在各自 antd
Select選擇器的 div#rc_select_0_list
(a) 之后今瀑。在當前這個場景里程梦, id
和文檔結構片斷是具有穩(wěn)定性質的点把。
進一步觀察,可以知道 div.ant-select-item
或 div.ant-select-item-option
可以定位下拉列表中的選項屿附。但是郎逃,如果想選中 yiminghe
(#2)選項,需要使用屬性選擇器 div[title="yiminghe"]
挺份。
綜上所述褒翰,點擊 #1
( input[aria-owns="rc_select_0_list"]
),再選中 #2
(div#rc_select_0_list + div.rc-virtual-list div[title="yiminghe"]
)匀泊,就完成了最初設定的UI自動化目標优训。
無論是哪一類選擇頁面元素的方法,選擇的本身并不重要各聘,最重要的是識別揣非、提煉與頁面元素相關的穩(wěn)定性質。頁面元素選擇的詞匯躲因,只不過是表達這種穩(wěn)定性質的一個手段拱她。
開發(fā)者給選擇元素帶來的影響
以上討論的選擇頁面元素的方法睹簇,基本上都強調了UI自動化沒有代碼侵入性的涵義通贞。假定和開發(fā)者之間井水不犯河水苛败,更適用于測試工程師(QA)做UI自動化的黑盒測試。
正如計算機超過 256k 內存箱靴,程序員們就不再精心設計內存的使用腺逛。自從前端框架和組件庫廣泛使用以來,尤其是以虛擬 DOM
樹為基礎的現(xiàn)代前端普及后衡怀,前端工程的開發(fā)效率有了質的提升棍矛。與此同時,大家對 HTML
標簽(tag)的語義化使用抛杨,卻不如早前那般重視够委。
除了編寫優(yōu)雅的 jsx
代碼,作為優(yōu)雅的前端工程師(FE)怖现,還應該特別關心 HTML
標簽(tag)茁帽、屬性和文檔結構的語義化表達。按鈕要用 <button class="ant-btn" />
屈嗤,而不是 <a class="ant-btn" />
潘拨。無序列表要用 <ul />
,有序列表用 <ol />
饶号,段落就用 <p />
铁追。如果需要對自己的主題做個解釋,就使用 <div title="主題" />
茫船。頁面元素的層次結構也如是琅束,是父子關系的內容就用標簽(tag)的父子結構表達扭屁,是兄弟關系就用標簽(tag)的兄弟關系表達語義。
如果開發(fā)者(前端工程師涩禀,F(xiàn)E)積極參與進來料滥,設計出優(yōu)雅的前端代碼,具有良好的可測性艾船,UI自動化就會吹進一股亞平寧半島的風葵腹。
與測試驅動開發(fā)(Test-Driven Development,TDD
)先寫單元測試用例再寫代碼實現(xiàn)類似屿岂,行為驅動開發(fā)(Behavior-Driven Development礁蔗,BDD
)也應該是先寫UI自動化的場景劇本,再進行前端代碼的開發(fā)雁社。既然是先有劇本(Feature
)和場景(scenario
),就會反向驅動前端工程師(FE)精心設計具有良好可測性的選擇器(Selector)晒骇,就會實現(xiàn)良好的語義化文檔結構和必要的 data-testid
等可測性屬性霉撵。
前端組件單元測試中,也需要準確選擇元素才能斷言洪囤,所以良好的單元測試也會為UI自動化選擇頁面元素提供良好的支持徒坡。
開發(fā)者還可以提供頁面元素選擇的框架級增強。例如瘤缩,可以根據(jù)需要增加 XPath
選擇 HTML
頁面元素的能力喇完,可以引入 Testing Library
等工具庫,強化UI自動化選擇頁面元素的能力剥啤。
Chrome
開發(fā)者工具(F12)的輔助作用
充分利用 Chrome
開發(fā)者工具(F12)提供的輔助能力锦溪,可以提高選擇頁面元素的效率。
選擇器(Selector)輔助分析
如下圖府怯,在需要分析的頁面元素(1)上點擊右鍵刻诊,從右鍵菜單中選擇 檢查
(2),即可用來分析頁面元素牺丙。
利用 Chrome
開發(fā)者工具(F12)则涯,復制出頁面元素的選擇器(Selector),如下圖:
在頁面元素(1)對應的 Elements
標簽(2)中的元素結構(3)上冲簿,右鍵菜單選擇 Copy
(4)粟判,選擇 Copy selector
(5),就可以復制出頁面元素(1)的選擇器(Selector)峦剔。
這個方法獲復制出的選擇器(Selector)档礁,缺少UI自動化所需要的穩(wěn)定性質,很難直接使用羊异。但是事秀,有很重要的參考價值彤断,能從中識別和提煉出具有穩(wěn)定性質的選擇器(Selector)。
選擇器(Selector)的驗證
對于比較復雜的頁面元素選擇器(Selector)易迹,我們需要驗證一下宰衙,才能用于UI自動化。畢竟睹欲,如果捕獲不到指定的元素供炼,選擇器(Selector)長得再好看也沒有用。
Puppeteer
技術棧選擇元素是由 document.querySelector
窘疮、 document.querySelectorAll
實現(xiàn)的袋哼,可以在 Chrome
開發(fā)者工具(F12)中使用同樣的方法來驗證選擇器(Selector)是否有效。如果開發(fā)者對選擇元素提供了框架級增強闸衫,也可以由其提供更多的驗證方法涛贯。
在這里,我們且只考慮CSS選擇器的驗證蔚出,以 document.querySelector
為例弟翘,如下圖:
如果選擇器(3)正確,對應的頁面元素(1)會有反選效果骄酗。 回車確認后稀余, Console
控制臺可以回顯頁面元素的HTML代碼(2),鼠標移動到(2)后趋翻,選中的頁面元素(1)也會有反選效果睛琳。
除了上述方法驗證外,還可以在 Chrome
開發(fā)者工具(F12踏烙,開發(fā)者工具 > Elements)中使用 Ctrl + F
調出查詢文本框(1)师骗,把選擇器(Selector) Copy
進去驗證。如下圖:
以上的 2 個方法讨惩,可以驗證頁面上的靜態(tài)元素丧凤。有些元素是交互中才會動態(tài)出現(xiàn)的,就不太方便驗證步脓。
這種情況下愿待,可以為動態(tài)元素指定一些比較夸張的驗證樣式來驗證。如下圖:
在 Chrome
開發(fā)者工具(F12靴患,開發(fā)者工具 > Elements)的 Styles
視圖(1)右側點加號(2)仍侥,選中 inspector-stylesheet
(3),在視圖中添加 CSS
選擇器(Selector)(4)鸳君。在這個樣式中农渊,可以寫比較夸張的樣式,例如很大的字(font-size: 50px)或颊。當觀察到頁面元素產(chǎn)生了預期的夸張變化后砸紊,說明你寫的選擇器(Selector)是對的传于。
這個驗證方式,也有不適用的場景醉顽。例如沼溜,CSS
選擇器優(yōu)先級的權重不夠高,即使是正確的選擇器(Selector)游添,元素樣式也不會出現(xiàn)預期的夸張變化系草。
在UI自動化測試中,選擇頁面元素是一件充滿詩意的工作唆涝,探索和研究不可能一勞永逸找都。