Selenium是ThoughtWorks公司研發的一個強大的基于浏覽器的開源自動化測試工具,它通常用來編寫web應用的自動化測試。早期也即Selenium1.x時期主要使用Selenium RC(Selenium Remote Control)進來自動化測試。Selenium2.x內建了Selenium和WebDriver的功能。
下圖所示為Selenium RC的工作原理:

(1)Selenium Server
Selenium Server負責控制浏覽器行為,總的來說,Selenium Server主要包括3個部分:Launcher、Http Proxy、Selenium Core。其中Selenium Core是被Selenium Server注入到浏覽器頁面中的,它其實就是一堆Javascript函數的集合。自動化測試的過程是:Selenium RC啟動一個Selenium Server,将操作web元素的API調用轉化為一段段Javascript,在Selenium核心啟動浏覽器之後注入這段Javascript函數即Selenium Core,通過這些Javascript函數,我們才可以實作用程式對浏覽器進行操作。(Javascript可以擷取并調用頁面的任何元素,自如的進行操作)
(2)Client Libraries
寫測試用例時用來控制Selenium Server的庫。測試用例通過調用Client Libraries來編寫相關的代碼。
工作流程如下圖所示:
具體過程為:
(1)測試用例通過Client Libraries的接口向Selenium Server發送Http請求,要求和Selenium Server建立連接配接
(2)Selenium Server的Launch啟動浏覽器,把Selenium Core加載入浏覽器頁面中,并發浏覽器的代理設定為Selenium Server的Http Proxy。
(3)測試用例通過Client Libraries的接口向Selenium Server發送Http請求,Selenium Server對請求進行解析,然後通過Http Proxy發送JS指令通知Selenium Core執行操作浏覽器的動作。
(4)Selenium Core接收到指令後,執行操作
(5)浏覽器收到新的頁面請求資訊,于是發送Http請求,請求新的web頁面。Selenium Server會接收到所有由它啟動的浏覽器發動的請求。
(6)Selenium Server接收到浏覽器發送的Http請求後,自己重組Http請求,擷取對應的web頁面
(7)Selenium Server的Http Proxy把接收的Web頁面傳回給浏覽器
(1)Selenium RC不能處理本機鍵盤和滑鼠事件
(2)Selenium RC不能處理彈出框、對話框(基本身份認證、檔案上傳/下載下傳)事件
(3)Selenium RC使用Javascript注入技術,速度不夠理想,穩定性大大依賴于Selenium核心對API翻譯成的Javascript品質高低。
WebDriver提供了另外一種方式與浏覽器進行互動。那就是利用浏覽器原生的API,封裝成一套更加面向對象的Selenium WebDriver API,直接操作浏覽器頁面裡的元素,甚至操作浏覽器本身(截屏,視窗大小,啟動,關閉,安裝插件,配置證書之類的)。由于使用的是浏覽器的原生API,速度大大提高,而且調用的穩定性交給了浏覽器廠商本身,顯然是更加科學。然而帶來的一些副作用就是,不同的浏覽器廠商,對Web元素的操作和呈現存在不同程度的差異,這就要求Selenium WebDriver要分浏覽器廠商的不同,提供不同的實作,例如Chrome有專門的ChromeDriver,Firefox有FirefoxDriver等等。
WebDriver Wire協定是通用的,也就是說不管是FirefoxDriver還是ChromeDriver,啟動之後都會在某一個端口啟動基于這套協定的Web Service。例如ChromeDriver初始化成功之後,預設會從<code>http://localhost:46350</code>開始,而<code>FirefoxDriver從http://localhost:7055</code>開始。後續我們調用WebDriver的任何API,都需要借助一個ComandExecutor發送一個指令,實際上是一個HTTP request給監聽端口上的Web Service。在我們的HTTP request的body中,會以WebDriver Wire協定規定的JSON格式的字元串來告訴Selenium我們希望浏覽器接下來做什麼事情。
具體過程如下:
(1)執行個體化WebDriver,Selenium首先會确認浏覽器的native component是否存在可用而且版本比對。若比對則在目标浏覽器裡啟動一整套Web Service。這套Web Service使用了Selenium自己設計定義的協定,名字叫做The WebDriver Wire Protocol。這套協定非常之強大,幾乎可以操作浏覽器做任何事情,包括打開、關閉、最大化、最小化、元素定位、元素點選、檔案上傳等等
(2)發送請求時,用WebDriver的HttpCommandExecutor類将指令轉換為URL作為value,指令作為key一起存入map作為request,同時會在request的body中存放相應的By Xpath、id、name。實際發送的URL都是相對路徑,字尾多以/session/:sessionId開頭,這也意味着WebDriver每次啟動浏覽器都會配置設定一個獨立的sessionId,多線程并行的時候彼此之間不會有沖突和幹擾。比如我們常用到的find_element_by_class_name這個接口,會轉化為/session/:sessionId/element這個url,然後在發出Http Request Body内再附上具體的參數,比如class name的值。比如我們要通路某一個網站,請求位址為:<code>http://localhost:46350/wd/hub/session/sessionId/url</code>,請求json内容:<code>{"url":"http://www.qq.com"}</code>。比如查找一個classname為test的元素,請求位址字尾為<code>/session/sessionId/element</code>,json内容<code>{"using":"class_name","value":"test"}</code>。
(3)收到并執行了這個操作之後,也會回複一個Http Response。内容也是Json,會傳回找到的element的各種細節,比如text、CSS selector、tag name、class name等等。比如: