一、自動化實現原理
1.創建驅動對象
(1) 首先加載瀏覽器安裝目錄下的exe文件
(2) 其次是加載可執行驅動的exe文件,監聽等待客戶端發送的web service請求.
底層原理如下: 1.在自動化測試過程中,存在三部分組件:客戶端腳本+驅動+瀏覽器終端。 2.驅動文件,以geckodriver.exe為例,這個可執行的驅動文件啟動后,相當於一個暴露了一系列接口的服務器,監聽某一端口,例如:89890。 3.客戶端的操作(訪問頁面,定位元素,輸入數據,點擊按鈕等)都是封裝成了接口請求(eg:/session/xx/yy),然后提交到驅動服務器。 4.驅動服務器接收到客戶端的請求后,再跟終端瀏覽器交互。 5.終端瀏覽器做出相應操作。
二、driver調用方法小結
三、Selenium 1.0 的工作原理
Selenium 1.0,又稱Selenium RC ,RC是Remote Control的縮寫。Selenium RC利用的原理:JavaScript代碼可以方便的獲取頁面上的任意元素並執行各種操作。
但是因為“同源政策(Same-origin policy)”(只有來自相同域名、端口和協議的javaScript代碼才能被瀏覽器執行),所以,要想在測試用例運行中的瀏覽器中,注入javascript代碼,從而實現自動化web操作,Selenium RC必須“欺騙”被測站點,讓它誤以為被注入的代碼是同源的。
那如何實現“欺騙”呢?這就是需要引入 Selenium RC Server 的原因了。其中的 Http Proxy 模塊就是來「欺騙」瀏覽器的。除了 Selenium RC Server,Selenium RC 方案的另一部分就是 Client Libraries。他們的具體關系如下圖1所示:
圖1 Selenium RC 的基本模塊
Selenium RC Server,主要包括Selenium Core,Http Proxy 和Launcher 三部分:
-
Selenium Core,是被注入到瀏覽器頁面中的JavaScript 函數集合,用來實現界面元素的識別和操作;
-
Http Proxy,作為代理服務器修改JavaScrip的源,以達到“欺騙”被測站點的目的;
- Launcher,用來在啟動測試瀏覽器時完成,Selenium Core 的注入和瀏覽器代理的設置。
Client Libraries,是測試用例代碼向Selenium RC Server發送 Http 請求的接口,支持多種語言,包括 Java、C# 和 Ruby 等。
Selenium執行流程圖,如圖2:
圖2 Selenium RC 的執行流程
-
測試用例通過基於不同語言的 Client Libraries向 Selenium RC Server 發送Http請求,要求與其建立連接。
-
連接建立后,Selenium RC Server 的Launcher 就會啟動瀏覽器或者重用之前已經打開的瀏覽器,把 Selenium Core(JavaScript 函數的集合)加載到瀏覽器頁面當中,並同時把瀏覽器的代理設置為Http Proxy。
-
測試用例通過 Client Libraries,向 Selenium RC Server 發送 Http請求,Selenium RC Server 解析請求,然后通過 Http Proxy 發送 JavaScript命令通知 Selenium Core 執行瀏覽器上控件的具體操作。
- Selenium Core 接收到指令后,執行操作。
-
如果瀏覽器收到新的頁面請求信息,則會發送 Http 請求來請求新的 Web 頁面。由於 Launcher 在啟動瀏覽器時把 Http Proxy 設置成為了瀏覽器的代理,所以 Selenium RC Server 會接收到所有由它啟動的瀏覽器發送的請求。
-
Selenium RC Server 接收到瀏覽器發送的 Http 請求后,重組 Http 請求以規避“同源策略”,然后獲取對應的 Web 頁面。
-
Http Proxy 把接收的 Web 頁面返回給瀏覽器,瀏覽器對接收的頁面進行渲染。
四、Selenium 2.0 的工作原理
Selenium 2.0,又稱 Selenium WebDriver,其原理是:使用瀏覽器原生的 WebDriver 實現頁面操作。實現方式完全不同於 Selenium 1.0。Selenium WebDriver 是典型的 Server-Client 模式,Server 端就是 Remote Server。以下是 Selenium 2.0 工作原理:
圖3 Selenium WebDriver 的執行流程
- 當使用 Selenium 2.0 啟動瀏覽器時,后台會同時啟動基於 WebDriver Wire 協議的 Web Service 作為 Selenium 的 Remote Server,並與瀏覽器綁定。之后,Remote Server 就開始監聽 Client 端的操作請求;
- 執行測試時,測試用例會作為 Client 端,每個請求都會被封裝成為一個command命令,每一個command命令都會對應一個web service服務地址,根據不同的command,獲取到請求地址,並封裝成httpRequest對象,以 Http Request 的方式發送給 Remote Server 。該 Http Request 的 body,是以 WebDriver Wire 協議規定的 JSON 格式來描述需要瀏覽器執行的具體操作;
- Remote Server 接收到請求后,通過java客戶端完成接口的調用:client.execute(httpRequest)獲得響應,,並將結果發給 WebDriver,由WebDriver 實際執行瀏覽器的操作;
- WebDriver 可以看做是直接操作瀏覽器的原生組件(Native Component),所以搭建測試環境時,通常都需要先下載瀏覽器對應的 WebDriver。
五、以findElement為例Debug方式代碼走讀:
driver.get("https://www.baidu.com"); WebElement element = driver.findElement(By.id("kw")); element.sendKeys("自動化測試");
實際,底層請求時,每個請求會被封裝為一個command,然后根據不同的commannd封裝得到不同的HttpRequest對象:
根據此命令,得到接口地址:
拿到此接口地址封裝為一個HttpRequest請求。
client.execute(httpRequest,true),執行接口調用:
至於其他操作:往輸入框輸入數據,點擊按鈕等,都是對應一個接口地址,通過調用接口,請求驅動來處理,最后驅動同瀏覽器進行交互,瀏覽器按照指示做出對應操作。
Selenium有一個類AbstractHttpCommandCodec,此類中維護了眾多自動化操作對應的接口地址:
public AbstractHttpCommandCodec() { defineCommand(STATUS, get("/status")); defineCommand(GET_ALL_SESSIONS, get("/sessions")); defineCommand(NEW_SESSION, post("/session")); defineCommand(GET_CAPABILITIES, get("/session/:sessionId")); defineCommand(QUIT, delete("/session/:sessionId")); defineCommand(GET_SESSION_LOGS, post("/logs")); defineCommand(GET_LOG, post("/session/:sessionId/log")); defineCommand(GET_AVAILABLE_LOG_TYPES, get("/session/:sessionId/log/types")); defineCommand(SWITCH_TO_FRAME, post("/session/:sessionId/frame")); defineCommand(SWITCH_TO_PARENT_FRAME, post("/session/:sessionId/frame/parent")); defineCommand(CLOSE, delete("/session/:sessionId/window")); defineCommand(SWITCH_TO_WINDOW, post("/session/:sessionId/window")); defineCommand(FULLSCREEN_CURRENT_WINDOW, post("/session/:sessionId/window/fullscreen")); defineCommand(GET_CURRENT_URL, get("/session/:sessionId/url")); defineCommand(GET, post("/session/:sessionId/url")); defineCommand(GO_BACK, post("/session/:sessionId/back")); defineCommand(GO_FORWARD, post("/session/:sessionId/forward")); defineCommand(REFRESH, post("/session/:sessionId/refresh")); defineCommand(SET_ALERT_CREDENTIALS, post("/session/:sessionId/alert/credentials")); defineCommand(UPLOAD_FILE, post("/session/:sessionId/file")); defineCommand(SCREENSHOT, get("/session/:sessionId/screenshot")); defineCommand(ELEMENT_SCREENSHOT, get("/session/:sessionId/screenshot/:id")); defineCommand(GET_TITLE, get("/session/:sessionId/title")); defineCommand(FIND_ELEMENT, post("/session/:sessionId/element")); defineCommand(FIND_ELEMENTS, post("/session/:sessionId/elements")); defineCommand(GET_ELEMENT_PROPERTY, get("/session/:sessionId/element/:id/property/:name")); defineCommand(CLICK_ELEMENT, post("/session/:sessionId/element/:id/click")); defineCommand(CLEAR_ELEMENT, post("/session/:sessionId/element/:id/clear")); defineCommand( GET_ELEMENT_VALUE_OF_CSS_PROPERTY, get("/session/:sessionId/element/:id/css/:propertyName")); defineCommand(FIND_CHILD_ELEMENT, post("/session/:sessionId/element/:id/element")); defineCommand(FIND_CHILD_ELEMENTS, post("/session/:sessionId/element/:id/elements")); defineCommand(IS_ELEMENT_ENABLED, get("/session/:sessionId/element/:id/enabled")); defineCommand(ELEMENT_EQUALS, get("/session/:sessionId/element/:id/equals/:other")); defineCommand(GET_ELEMENT_RECT, get("/session/:sessionId/element/:id/rect")); defineCommand(GET_ELEMENT_LOCATION, get("/session/:sessionId/element/:id/location")); defineCommand(GET_ELEMENT_TAG_NAME, get("/session/:sessionId/element/:id/name")); defineCommand(IS_ELEMENT_SELECTED, get("/session/:sessionId/element/:id/selected")); defineCommand(GET_ELEMENT_SIZE, get("/session/:sessionId/element/:id/size")); defineCommand(GET_ELEMENT_TEXT, get("/session/:sessionId/element/:id/text")); defineCommand(SEND_KEYS_TO_ELEMENT, post("/session/:sessionId/element/:id/value")); defineCommand(GET_ALL_COOKIES, get("/session/:sessionId/cookie")); defineCommand(GET_COOKIE, get("/session/:sessionId/cookie/:name")); defineCommand(ADD_COOKIE, post("/session/:sessionId/cookie")); defineCommand(DELETE_ALL_COOKIES, delete("/session/:sessionId/cookie")); defineCommand(DELETE_COOKIE, delete("/session/:sessionId/cookie/:name")); defineCommand(SET_TIMEOUT, post("/session/:sessionId/timeouts")); defineCommand(SET_SCRIPT_TIMEOUT, post("/session/:sessionId/timeouts/async_script")); defineCommand(IMPLICITLY_WAIT, post("/session/:sessionId/timeouts/implicit_wait")); defineCommand(GET_APP_CACHE_STATUS, get("/session/:sessionId/application_cache/status")); defineCommand(IS_BROWSER_ONLINE, get("/session/:sessionId/browser_connection")); defineCommand(SET_BROWSER_ONLINE, post("/session/:sessionId/browser_connection")); defineCommand(GET_LOCATION, get("/session/:sessionId/location")); defineCommand(SET_LOCATION, post("/session/:sessionId/location")); defineCommand(GET_SCREEN_ORIENTATION, get("/session/:sessionId/orientation")); defineCommand(SET_SCREEN_ORIENTATION, post("/session/:sessionId/orientation")); defineCommand(GET_SCREEN_ROTATION, get("/session/:sessionId/rotation")); defineCommand(SET_SCREEN_ROTATION, post("/session/:sessionId/rotation")); defineCommand(IME_GET_AVAILABLE_ENGINES, get("/session/:sessionId/ime/available_engines")); defineCommand(IME_GET_ACTIVE_ENGINE, get("/session/:sessionId/ime/active_engine")); defineCommand(IME_IS_ACTIVATED, get("/session/:sessionId/ime/activated")); defineCommand(IME_DEACTIVATE, post("/session/:sessionId/ime/deactivate")); defineCommand(IME_ACTIVATE_ENGINE, post("/session/:sessionId/ime/activate")); // Mobile Spec defineCommand(GET_NETWORK_CONNECTION, get("/session/:sessionId/network_connection")); defineCommand(SET_NETWORK_CONNECTION, post("/session/:sessionId/network_connection")); defineCommand(SWITCH_TO_CONTEXT, post("/session/:sessionId/context")); defineCommand(GET_CURRENT_CONTEXT_HANDLE, get("/session/:sessionId/context")); defineCommand(GET_CONTEXT_HANDLES, get("/session/:sessionId/contexts")); }
參考博客原地址:https://www.cnblogs.com/miaojjblog/p/10563058.html
參考博客原地址:https://www.cnblogs.com/nickjiang/p/9332480.html
學習參考博客總結,以備后面復習,分享給需要的人,不足之處后續修正!