【java+selenium3】自動化基礎小結+selenium原理揭秘 (十七)


一、自動化實現原理

  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 的執行流程

  1. 測試用例通過基於不同語言的 Client Libraries向 Selenium RC Server 發送Http請求,要求與其建立連接。

  2. 連接建立后,Selenium RC Server 的Launcher 就會啟動瀏覽器或者重用之前已經打開的瀏覽器,把 Selenium Core(JavaScript 函數的集合)加載到瀏覽器頁面當中,並同時把瀏覽器的代理設置為Http Proxy。

  3. 測試用例通過 Client Libraries,向 Selenium RC Server 發送 Http請求,Selenium RC Server 解析請求,然后通過 Http Proxy 發送 JavaScript命令通知 Selenium Core 執行瀏覽器上控件的具體操作。

  4. Selenium Core 接收到指令后,執行操作。
  5. 如果瀏覽器收到新的頁面請求信息,則會發送 Http 請求來請求新的 Web 頁面。由於 Launcher 在啟動瀏覽器時把 Http Proxy 設置成為了瀏覽器的代理,所以 Selenium RC Server 會接收到所有由它啟動的瀏覽器發送的請求。

  6. Selenium RC Server 接收到瀏覽器發送的 Http 請求后,重組 Http 請求以規避“同源策略”,然后獲取對應的 Web 頁面。

  7. 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 的執行流程

  1. 當使用 Selenium 2.0 啟動瀏覽器時,后台會同時啟動基於 WebDriver Wire 協議的 Web Service 作為 Selenium 的 Remote Server,並與瀏覽器綁定。之后,Remote Server 就開始監聽 Client 端的操作請求;
  2. 執行測試時,測試用例會作為 Client 端,每個請求都會被封裝成為一個command命令,每一個command命令都會對應一個web service服務地址,根據不同的command,獲取到請求地址,並封裝成httpRequest對象,以 Http Request 的方式發送給 Remote Server 。該 Http Request 的 body,是以 WebDriver Wire 協議規定的 JSON 格式來描述需要瀏覽器執行的具體操作;
  3. Remote Server 接收到請求后,通過java客戶端完成接口的調用:client.execute(httpRequest)獲得響應,,並將結果發給 WebDriver,由WebDriver 實際執行瀏覽器的操作;
  4. 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

學習參考博客總結,以備后面復習,分享給需要的人,不足之處后續修正!


免責聲明!

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



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