通過(guò)研究selenium-webdriver的源碼,筆者發(fā)現(xiàn)其實(shí)webdriver的實(shí)現(xiàn)原理并不高深莫測(cè)無(wú)法揣度。在這里以webdriver ruby binding的firefox-webdriver實(shí)現(xiàn)為例,簡(jiǎn)單介紹一下webdriver的工作原理。
-
當(dāng)測(cè)試腳本啟動(dòng)firefox的時(shí)候,selenium-webdriver 會(huì)首先在新線(xiàn)程中啟動(dòng)firefox瀏覽器。如果測(cè)試腳本指定了firefox的profile,那么就以該profile啟動(dòng),否則的話(huà)就新啟1個(gè)profile,并啟動(dòng)firefox;
-
firefox一般是以-no-remote的方法啟動(dòng),啟動(dòng)后selenium-webdriver會(huì)將firefox綁定到特定的端口,綁定完成后該firefox實(shí)例便作為webdriver的remote server存在;
-
客戶(hù)端(也就是測(cè)試腳本)創(chuàng)建1個(gè)session,在該session中通過(guò)http請(qǐng)求向remote server發(fā)送restful的請(qǐng)求,remote server解析請(qǐng)求,完成相應(yīng)操作并返回response;
-
客戶(hù)端接受response,并分析其返回值以決定是轉(zhuǎn)到第3步還是結(jié)束腳本;
這就是webdriver的工作流程,看起來(lái)很復(fù)雜實(shí)際上當(dāng)了解了webdriver的實(shí)現(xiàn)原理后,理解上述問(wèn)題應(yīng)該比較簡(jiǎn)單。
webdriver是按照server – client的經(jīng)典設(shè)計(jì)模式設(shè)計(jì)的。
server端就是remote server,可以是任意的瀏覽器。當(dāng)我們的腳本啟動(dòng)瀏覽器后,該瀏覽器就是remote server,它的職責(zé)就是等待client發(fā)送請(qǐng)求并做出相應(yīng);
client端簡(jiǎn)單說(shuō)來(lái)就是我們的測(cè)試代碼,我們測(cè)試代碼中的一些行為,比如打開(kāi)瀏覽器,轉(zhuǎn)跳到特定的url等操作是以http請(qǐng)求的方式發(fā)送給被測(cè)試瀏覽器,也就是remote server;remote server接受請(qǐng)求,并執(zhí)行相應(yīng)操作,并在response中返回執(zhí)行狀態(tài)、返回值等信息;
舉個(gè)實(shí)際的例子,下面代碼的作用是”命令”firefox轉(zhuǎn)跳到google主頁(yè):
driver = Selenium::WebDriver.for :firefox
driver.navigate.to "http://google.com"
在執(zhí)行driver.navigate.to “http://google.com” 這句代碼時(shí),client,也就是我們的測(cè)試代碼向remote server發(fā)送了如下的請(qǐng)求:
POST session/285b12e4-2b8a-4fe6-90e1-c35cba245956/url
post_data {"url":"http://google.com"}
通過(guò)post的方式請(qǐng)求localhost:port/hub/session/session_id/url地址,請(qǐng)求瀏覽器完成跳轉(zhuǎn)url的操作。
如果上述請(qǐng)求是可接受的,或者說(shuō)remote server是實(shí)現(xiàn)了這個(gè)接口,那么remote server會(huì)跳轉(zhuǎn)到該post data包含的url,并返回如下的response
{"name":"get","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":""}
該response中包含如下信息
-
name:remote server端的實(shí)現(xiàn)的方法的名稱(chēng),這里是get,表示跳轉(zhuǎn)到指定url;
-
sessionId:當(dāng)前session的id;
-
status:請(qǐng)求執(zhí)行的狀態(tài)碼,非0表示未正確執(zhí)行,這里是0,表示一切ok不許擔(dān)心;
-
value:請(qǐng)求的返回值,這里返回值為空,如果client調(diào)用title接口,則該值應(yīng)該是當(dāng)前頁(yè)面的title;
如果client發(fā)送的請(qǐng)求是定位某個(gè)特定的頁(yè)面元素,則response的返回值可能是這樣的:
{"name":"findElement","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":{"ELEMENT":"{2192893e-f260-44c4-bdf6-7aad3c919739}"}}
name,sessionId,status跟上面的例子是差不多的,區(qū)別是該請(qǐng)求的返回值是ELEMENT:{2192893e-f260-44c4-bdf6-7aad3c919739},表示定位到元素的id,通過(guò)該id,client可以發(fā)送如click之類(lèi)的請(qǐng)求與server端進(jìn)行交互。
那么remote server端的這些功能是如何實(shí)現(xiàn)的呢?答案是瀏覽器實(shí)現(xiàn)了webdriver的統(tǒng)一接口,這樣client就可以通過(guò)統(tǒng)一的restful的接口去進(jìn)行瀏覽器的自動(dòng)化操作。目前webdriver支持ie, chrome, firefox, opera等主流瀏覽器,其主要原因是這些瀏覽器實(shí)現(xiàn)了webdriver約定的各種接口。
具體見(jiàn)http://code.google.com/p/selenium/wiki/JsonWireProtocol#Command_Reference。
由于筆者能力有限才疏學(xué)淺,因此文中定然有些謬誤之處,還望不吝指出,多多斧正之。