深入了解selenium及webdriver原理

最近正在編寫selenium?webdriver自動(dòng)化框架,經(jīng)過幾天的努力,目前基本已經(jīng)實(shí)現(xiàn)了一套即能滿足數(shù)據(jù)驅(qū)動(dòng)、又能滿足Web關(guān)鍵字驅(qū)動(dòng)的自動(dòng)化框架(主要基于?ant+jenkins+testng+selenium webdriver+jxl實(shí)現(xiàn))。通過這次的自動(dòng)化框架開發(fā),我深刻的發(fā)現(xiàn)了webdriver的強(qiáng)大,甚至我們可以看到阿里巴巴的F2etest瀏覽器兼容性測(cè)試平臺(tái)也是基于webdriver。以下特別轉(zhuǎn)載了一篇關(guān)于selenium webdriver的介紹,讓我們從深層次理解webdriver:

selenium與webdriver整合后,形成的新的測(cè)試工具叫做selenium2.x。在selenium1時(shí)間,selenium使用JavaScript來達(dá)到測(cè)試自動(dòng)化的目標(biāo)。

早期的Selenium使用的是javascript注入技術(shù)與瀏覽器打交道,需要Selenium RC啟動(dòng)一個(gè)Server,將操作Web元素的API調(diào)用轉(zhuǎn)化為一段段Javascript,在Selenium內(nèi)核啟動(dòng)瀏覽器之后注入這段Javascript。開發(fā)過Web應(yīng)用的人都知道,Javascript可以獲取并調(diào)用頁面的任何元素,自如的進(jìn)行操作。由此才實(shí)現(xiàn)了Selenium的目的:自動(dòng)化Web操作。這種Javascript注入技術(shù)的缺點(diǎn)是速度不理想,而且穩(wěn)定性大大依賴于Selenium內(nèi)核對(duì)API翻譯成的Javascript質(zhì)量高低。

當(dāng)Selenium2.x 提出了WebDriver的概念之后,它提供了完全另外的一種方式與瀏覽器交互。那就是利用瀏覽器原生的API,封裝成一套更加面向?qū)ο蟮腟elenium WebDriver API,直接操作瀏覽器頁面里的元素,甚至操作瀏覽器本身(截屏,窗口大小,啟動(dòng),關(guān)閉,安裝插件,配置證書之類的)。由于使用的是瀏覽器原生的API,速度大大提高,而且調(diào)用的穩(wěn)定性交給了瀏覽器廠商本身,顯然是更加科學(xué)。然而帶來的一些副作用就是,不同的瀏覽器廠商,對(duì)Web元素的操作和呈現(xiàn)多少會(huì)有一些差異,這就直接導(dǎo)致了Selenium WebDriver要分瀏覽器廠商不同,而提供不同的實(shí)現(xiàn)。例如Firefox就有專門的FirefoxDriver,Chrome就有專門的ChromeDriver等等。(甚至包括了AndroidDriver和iOS WebDriver)

WebDriver Wire協(xié)議是通用的,也就是說不管是FirefoxDriver還是ChromeDriver,啟動(dòng)之后都會(huì)在某一個(gè)端口啟動(dòng)基于這套協(xié)議的Web Service。例如FirefoxDriver初始化成功之后,默認(rèn)會(huì)從http://localhost:7055開始,而ChromeDriver則大概是http://localhost:46350之類的。接下來,我們調(diào)用WebDriver的任何API,都需要借助一個(gè)ComandExecutor發(fā)送一個(gè)命令,實(shí)際上是一個(gè)HTTP request給監(jiān)聽端口上的Web Service。在我們的HTTP request的body中,會(huì)以WebDriver Wire協(xié)議規(guī)定的JSON格式的字符串來告訴Selenium我們希望瀏覽器接下來做社么事情。

在我們new一個(gè)WebDriver的過程中,Selenium首先會(huì)確認(rèn)瀏覽器的native component是否存在可用而且版本匹配。接著就在目標(biāo)瀏覽器里啟動(dòng)一整套Web Service,這套Web Service使用了Selenium自己設(shè)計(jì)定義的協(xié)議,名字叫做The WebDriver Wire Protocol。這套協(xié)議非常之強(qiáng)大,幾乎可以操作瀏覽器做任何事情,包括打開、關(guān)閉、最大化、最小化、元素定位、元素點(diǎn)擊、上傳文件等等等等。

這里筆者初步畫了一個(gè)圖來表示各種WebDriver的工作原理:

從上圖中我們可以看出,不同瀏覽器的WebDriver子類,都需要依賴特定的瀏覽器原生組件,例如Firefox就需要一個(gè)add-on名字叫webdriver.xpi。而IE的話就需要用到一個(gè)dll文件來轉(zhuǎn)化Web Service的命令為瀏覽器native的調(diào)用。另外,圖中還標(biāo)明了WebDriver Wire協(xié)議是一套基于RESTful的web service。

關(guān)于WebDriver Wire協(xié)議的細(xì)節(jié),比如希望了解這套Web Service能夠做哪些事情,可以閱讀Selenium官方的協(xié)議文檔, 在Selenium的源碼中,我們可以找到一個(gè)HttpCommandExecutor這個(gè)類,里面維護(hù)了一個(gè)Map,它負(fù)責(zé)將一個(gè)個(gè)代表命令的簡(jiǎn)單字符串key,轉(zhuǎn)化為相應(yīng)的URL,因?yàn)镽EST的理念是將所有的操作視作一個(gè)個(gè)狀態(tài),每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)URI。所以當(dāng)我們以特定的URL發(fā)送HTTP request給這個(gè)RESTful web service之后,它就能解析出需要執(zhí)行的操作。截取一段源碼如下:

nameToUrl = ImmutableMap.builder()

? ? ? ? .put(NEW_SESSION, post("/session"))

? ? ? ? .put(QUIT, delete("/session/:sessionId"))

? ? ? ? .put(GET_CURRENT_WINDOW_HANDLE, get("/session/:sessionId/window_handle"))

? ? ? ? .put(GET_WINDOW_HANDLES, get("/session/:sessionId/window_handles"))

? ? ? ? .put(GET, post("/session/:sessionId/url"))

? ? ? ? ? ? // The Alert API is still experimental and should not be used.

? ? ? ? .put(GET_ALERT, get("/session/:sessionId/alert"))

? ? ? ? .put(DISMISS_ALERT, post("/session/:sessionId/dismiss_alert"))

? ? ? ? .put(ACCEPT_ALERT, post("/session/:sessionId/accept_alert"))

? ? ? ? .put(GET_ALERT_TEXT, get("/session/:sessionId/alert_text"))

? ? ? ? .put(SET_ALERT_VALUE, post("/session/:sessionId/alert_text"))

? 可以看到實(shí)際發(fā)送的URL都是相對(duì)路徑,后綴多以/session/:sessionId開頭,這也意味著WebDriver每次啟動(dòng)瀏覽器都會(huì)分配一個(gè)獨(dú)立的sessionId,多線程并行的時(shí)候彼此之間不會(huì)有沖突和干擾。例如我們最常用的一個(gè)WebDriver的API,getWebElement在這里就會(huì)轉(zhuǎn)化為/session/:sessionId/element這個(gè)URL,然后在發(fā)出的HTTP request body內(nèi)再附上具體的參數(shù)比如by ID還是CSS還是Xpath,各自的值又是什么。收到并執(zhí)行了這個(gè)操作之后,也會(huì)回復(fù)一個(gè)HTTP response。內(nèi)容也是JSON,會(huì)返回找到的WebElement的各種細(xì)節(jié),比如text、CSS selector、tag name、class name等等。以下是解析我們說的HTTP response的代碼片段:

try {

? ? ? ? response = new JsonToBeanConverter().convert(Response.class, responseAsText);

? ? ? } catch (ClassCastException e) {

? ? ? ? if (responseAsText != null && "".equals(responseAsText)) {

? ? ? ? ? // The remote server has died, but has already set some headers.

? ? ? ? ? // Normally this occurs when the final window of the firefox driver

? ? ? ? ? // is closed on OS X. Return null, as the return value _should_ be

? ? ? ? ? // being ignored. This is not an elegant solution.

? ? ? ? ? return null;

? ? ? ? }

? ? ? ? throw new WebDriverException("Cannot convert text to response: " + responseAsText, e);

? ? ? } //...

? 相信總結(jié)道這里,應(yīng)該對(duì)WebDriver的運(yùn)行原理應(yīng)該清楚了!其實(shí)挺佩服這一套R(shí)ESTful web service的設(shè)計(jì)。感覺封裝WebDriver暴露出來的public API還可以更加友好跟強(qiáng)大一點(diǎn),這次就先總結(jié)道這里,會(huì)繼續(xù)分析Selenium源碼,繼續(xù)分享的!

? ? ? ? 關(guān)于使用Selenium WebDriver的經(jīng)驗(yàn)小結(jié):

其中WebDriver更加面向?qū)ο蟮姆绞酱蟠蠼档土薙elenium的入門門檻,對(duì)Web元素的操作也非常之簡(jiǎn)單易學(xué)。實(shí)際項(xiàng)目用起來,工作量最大的部分就是你如何解析定位到你的目標(biāo)項(xiàng)目頁面中的各種元素。好比你要定位一個(gè)Button,你可以用ID,可以用CSS,可以用XPATH,你為了點(diǎn)擊這個(gè)Button,寫了一個(gè)函數(shù)調(diào)用Selenium里的API,即WebElement里的click()或者submit(),那么另外一個(gè)Button怎么辦?成百上千個(gè)Button又怎么辦?

所以,你需要有一套自己實(shí)現(xiàn)的算法或者封裝,來根據(jù)項(xiàng)目頁面的特點(diǎn)提供一套通用的元素定位方式。當(dāng)你的通用定位邏輯能準(zhǔn)確的找到任何一個(gè)元素的時(shí)候,剩下的事情就順理成章了,交給Selenium WebElement的API就可以了。這一套定位邏輯筆者覺得才是使用Selenium做Web自動(dòng)化工作量最大的部分。當(dāng)然有的公司W(wǎng)eb項(xiàng)目使用了自己開發(fā)的UI框架,例如筆者所在的公司,這樣Web元素的定位規(guī)則和算法就比較容易設(shè)計(jì)。如果Web項(xiàng)目開發(fā)出來的頁面代碼比較雜亂無章,那么你就需要更加高明和嚴(yán)謹(jǐn)?shù)倪壿嬋ふ夷阆胍僮骱筒榭吹脑亓耍?/p>

在筆者的項(xiàng)目里,筆者自己設(shè)計(jì)并封裝了一套通用的API,去智能的定位頁面中的各種類型的元素。比如項(xiàng)目里的頁面有大量的dialog和wizard,都是用div+css實(shí)現(xiàn)的。我就提供了一個(gè)dialog組件,帶有next(),save(),finish(),click(String buttonName),cancel()等方法,然后根據(jù)遮罩層和loading Icon的時(shí)間來追蹤操作完成的進(jìn)度。這里只是舉個(gè)小小的例子,有機(jī)會(huì)再分享更多的細(xì)節(jié)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容