WebDriver工作原理


在我們new一個webdriver的過程中,Selenium首先會確認瀏覽器的nativa component是否存在可用而且版本匹配。接着就在目標瀏覽器里啟動一整套web service(實際上就是瀏覽器廠商提供的driver,比如IEDriver,ChromeDriver,他們都實現了WebDriver's wire protocol),這套web service使用了Selenium自己設計定義的協議,名字叫做The WebDriver Wire Protocol. 這套協議非常之強大,幾乎可以操作瀏覽器做任何事情,包括打開、關閉、最大化、最小化、元素定位、元素點擊、上傳文件等等。

WebDriver Wire是通用的,也就是說不管是FirefoxDriver還是ChromeDriver,啟動之后都會在某一個端口啟動基於這套協議的Web Service。例如FirefoxDriver初始化成功之后,默認會從http://localhost:7055開始,而ChromeDriver則大概是https://localhost:46350之類的。接下來,我們調用WebDriver的任何API,都需要借助一個CommandExecutor發送一個命令,實際上是一個HTTP request給監聽端口上的webservice。在我們的HTTP request的body中,會以webdriver wire協議規定的JSON格式的字符串來告訴Selenium我們希望瀏覽器接下來做什么事情。

可以更通俗的理解:由於客戶端腳本(java,python,ruby)不能直接和瀏覽器通信,這時候可以把webservice當做一個翻譯器,它可以把客戶端代碼翻譯成瀏覽器可以識別的代碼(比如js),客戶端(也就是測試腳本)創建一個session,在該session中通過http請求向webservice發送restful的請求,webservice翻譯成瀏覽器懂得腳本傳給瀏覽器,瀏覽器把執行的結果返回給webservice,webservice把返回的結果做了一些封裝(一般都是json格式),然后返回給client,根據返回值就能判斷對瀏覽器的操作是不是執行成功。

摘自官網對於chrome driver的描述:

The ChromeDriver consists of three seperate pieces. There is the browser itself("chrome"), the language bindings provided by the Selenium project("the driver") and an executable downloaded from the Chromium proejct which acts as a bridge between "chrome" and the "driver". This executable is called "chromedriver", but we'll try and refer to it as the "server" in this page to reduce confusion.

大概意思就是我們下載的chrome可執行文件(.exe) 是為作為瀏覽器與client(language binding)橋梁的作用,也更印證了對於webservice(driver)的理解。

 

舉個實際的例子:

WebDriver driver = new FirefoxDriver();

driver.get('http://www.google.com");

在執行driver.get('http://www.google.com");這句代碼時,client也就是我們的測試代碼向webservice(remote server)發送了如下的請求:

POST session/285b12e4-2b8a-4fe6-90e1-c35cba245956/url

post_data{"url":"http://google.com"}

通過POST的方式請求localhost:port/hub/session/session_id/url地址,請求瀏覽器完成跳轉url的操作。

如果上述請求是可接受的,或者說web service是實現了這個接口,那么web service會跳轉到該post data包含的url,並返回如下的response:

{"name":"get", "sessionid":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":""}

該response中包含如下信息

name: web service端的實現的方法的名稱,這里是get,表示跳轉到指定url;

sessionid:當前session的id;

status:請求執行的狀態碼,非0表示未正確執行,這里是0,表示一切ok不必擔心;

value:請求的返回值,這里返回值為空,如果client調用title接口,則該值應該是當前頁面的title;

如果client發送的請求是定位某個特定的頁面元素,則response的返回值可能是這樣的:

{"name":"findElement","sessionId":"285b12e4-2b8a-4fe6-90e1-c35cba245956","status":0,"value":{"ELEMENT":"{2192893e-f260-44c4-bdf6-7aad3c919739}"}}

name,sessionid,status跟上面的例子是差不多的,區別是該請求的返回值是ELEMENT:{2192893e-f260-44c4-bdf6-7aad3c919739},表示定位到元素的id,通過該id,client可以發送如click之類的請求與server端進行交互。

 

下圖表示了各種webdriver的工作原理

從上圖我們可以看出,不同瀏覽器的webdriver子類,都需要依賴特定的瀏覽器原生組件,例如運行firefox就需要一個add-on名字叫webdriver.xpi。而IE的話就需要用到一個dll文件來轉化web service的命令為瀏覽器native的調用。另外,圖中還標明了WebDriver wire協議是一套基於RESTFUL的web service.

 

關於WebDriver Wire協議的細節,比如希望了解這套Web Service能夠做哪些事情,可以閱讀Selenium官方的協議文檔,在Selenium的源碼中,我們可以找到一個HttpCommandExecutor這個類,里面維護了一個Map<String, CommandInfo>, 它負責將一個個代表命令的簡單字符串key,轉化為相應的URL,因為REST的理念是將所有的操作視作一個個狀態,每一個狀態對應一個URI。所以當我們以特定的URL發送HTTP request給這個RESTFUL web service之后,它就能解析出需要執行的操作。截取一段源碼如下:

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

PS:如果想更深入的了解WebDriver的架構,可以參考該文章http://www.aosabook.org/en/selenium.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM