Chrome remote debugging protocol在自动化测试中的应用和实践
從selenium說起
雖然我們的主題是cdp(chrome debug protocol)的應用,但在介紹cdp之前,不得不先從selenium說起,因為這兩者有密不可分的關系。
我們知道,在最新的selenium里,當你去執行一個測試動作,例如打開瀏覽器,然后輸入網址,找到一個搜索框填入文本并點擊搜索,這背后所依賴的技術,其實是webdriver,而當你的動作執行在chrome瀏覽器上,更為細化的說,依賴的是chromewebdriver。
我們詳細的來分析這一流程,你會更清楚的知道cdp與此有何關系。
首先我們來寫一個示例代碼:
from selenium import webdriverdriver = webdriver.Chrome() driver.get("https://www.baidu.com/")執行這段代碼,會看到系統啟動了chrome瀏覽器,并跳轉到了百度首頁。
看driver = webdriver.Chrome()這一句,以下是這段代碼的流程圖解。
可以看到在這個流程中,chromedriver起到的是橋梁的作用,他接受客戶端的請求,然后轉化為瀏覽器的標準指令操作瀏覽器,而在后半部分,也就是指令如何讓瀏覽器工作中,就涉及到了我們的主題cdp,因為這部分的標準其實就是cdp。
什么是cdp
chrome debug protocol,簡稱cdp。
大家應該都用過chrome瀏覽器的F12,也就是devtools,其實這是一個web應用,當你使用devtools的時候,瀏覽器本身會作為一個服務端,而你看到的瀏覽器調試工具界面,其實只是一個前端應用,在這中間通信的,就是cdp,他是基于websocket的,一個讓devtools和瀏覽器內核交換數據的通道。
cdp本身是可開放的,換句話說,你用devtools能做什么(例如操作瀏覽器,獲取網絡信息,獲取js覆蓋數據,獲取性能數據等等),你就能用cdp做什么。
cdp的官方文檔地址,可以點擊查閱,這里再簡單的介紹一下。
cdp把不同的操作劃分為了不同的域(domain),每個域負責不同的功能模塊,例如,Page域可以獲取當前頁面數據,或者操作頁面跳轉等等;Profiler域可以獲取當前的頁面的js覆蓋率數據等等;
直接引用FEX的一篇文章來解釋:
該協議把操作劃分為不同的域(domain),比如 DOM、Debugger、Network、Console 和 Timeline 等,可以理解為 DevTools 中的不同功能模塊。
每個域(domain)定義了它所支持的 command 和它所產生的 event。
每個 command 包含 request 和 response 兩部分,request 部分指定所要進行的操作以及操作說要的參數,response 部分表明操作狀態,成功或失敗。
command 和 event 中可能涉及到非基本數據類型,在 domain 中被歸為 Type,比如:’frameId’: <FrameId>,其中 FrameId 為非基本數據類型
至此,不難理解:
domain = command + event + type
使用cdp的方式
最原始的使用cdp的方式可以參照google的cdp文檔來:
1.使用附加參數打開chrome的遠程調試協議開關(普通模式下的chrome瀏覽器是無法直接使用cdp通信的,另外,請注意,在不同的操作系統下指令細節會有所不同),
chrome.exe --remote-debugging-port = 9222此時一個打開的遠程調試協議的瀏覽器實例被啟動。
2.為做演示,在打開的瀏覽器中,輸入百度的網址并進入,新開一個tab,進入網址http://localhost:9222,此時應該如截圖所示:
3.點擊百度這個標簽,進入他的devtools界面,看一下地址欄,記錄page/后面的通信標識值,然后在console里輸入以下代碼:
var ws = new WebSocket('ws://localhost:9222/devtools/page/這里填剛才記錄的標識值'); ws.send('{"id": 1, "method": "Page.navigate", "params": {"url": "http://www.soso.com"}}')執行完會發現,剛才的百度頁面,跳轉到了soso的頁面,其實這段代碼就是新開了一個websocket連接到剛才的百度頁面的調試地址,然后通過page域的navigate方法讓該頁面重新跳轉到了指定地址。
需要注意的一點是,在這里,每個tab(頁面)都只有一個單獨的通信地址,且每個地址只能與對應的tab通信。
以上就是比較原始的使用方法,實際上,cdp有很多封裝好的庫可以使用,例如python的PyChromeDevTools庫,nodejs的chrome-remote-interface庫等等,更多上層封裝庫請參見官方文檔。
cdp在自動化中的應用和實踐
看了以上內容,可能你會得出一個結論,selenium依賴webdriver,而在chrome瀏覽器中,webdriver又依賴chromedriver,chromedriver又是依賴cdp的;那么,我使用selenium和我直接使用cdp,有什么區別呢?
實際上真要較真(不怕麻煩)的話,是沒有區別的,但二者還是有一些差異的,selenium的封裝更為上層,使得你不用去關心原始的cdp到底如何使用,而且也集成了聚焦測試所需要的一些功能,例如分布式執行,docker image等等,使得在測試這個需求上,更為方便;而直接使用cdp的話,會讓整個結構更為簡潔,而且,有些操作由于webdriver沒有封裝(例如獲取性能數據,獲取js覆蓋率等等),所以直接使用cdp會更為精準。那么有沒有辦法讓二者的優點結合呢?
在這里我發現了兩種方案可以做到,
1.通過命令行啟動開啟了調試協議的chrome瀏覽器,然后在selenium里,初始化webdriver時指定ChromeOption的__debugger_address的值為之前的遠程調試地址,然后使用selenium操作webdriver,使用PyChromeDevTools操作cdp,示例代碼如下:
import os import PyChromeDevTools from selenium import webdrivercmd = "chrome.exe --remote-debugging-port=9222" os.popen(cmd) #此時chrome瀏覽器打開 time.sleep(3) chrome = PyChromeDevTools.ChromeInterface()#使用chrome操作cdp options = webdriver.ChromeOptions() options._debugger_address = "localhost:9222" driver = webdriver.Chrome(chrome_options=self.options)2.可以直接使用selenium的預留cdp通信方法execute_cdp_cmd,示例代碼如下:
from selenium import webdriverdriver = webdriver.Chrome() driver.get("https://www.baidu.com/") driver.execute_cdp_cmd('Page.navigate',{"url": "http://www.soso.com"})利用cdp獲取頁面網絡數據
有時候當腳本出錯了,我們會希望獲得更多的信息去排查,如果這時候能重現當時的網絡請求,那么排查會容易的多,下面是一個獲取頁面網絡數據(response值)的例子,這里只拿了請求的response值,但實際上稍加改動就可以把請求信息拿全(request+response),為了方便演示上面兩種方法,這里混用了上面的兩個方案。
from selenium import webdriver import time import os import PyChromeDevToolsos.chdir(r"C:\Users\zyj\AppData\Local\Google\Chrome SxS\Application") #這里是改變了當前環境變量 cmd = "chrome.exe --remote-debugging-port=9222" os.popen(cmd)#啟動chrome瀏覽器 time.sleep(3) chrome = PyChromeDevTools.ChromeInterface() options = webdriver.ChromeOptions() options._debugger_address = "localhost:9222" driver = webdriver.Chrome(chrome_options=options) chrome.Network.enable()#開啟頁面的網絡信息收集模式 time.sleep(2) driver.execute_cdp_cmd('Page.navigate',{"url": "http://www.mycaigou.com"})#跳轉到mycaigou,這里用的selenium的execute_cdp_cmd方法做到的 responseReceived = chrome.wait_event("Network.responseReceived", timeout=60)#等待response收集事件結束,獲取收集信息,這里的信息不包含詳細的response內容,需要用到方法getResponseBody resquest_id = responseReceived[0]['params']['requestId']#這個id是指你想要收集哪個請求的信息,他是請求的唯一標示,這里隨便拿了一個,沒做遍歷 res = chrome.Network.getResponseBody(requestId=resquest_id)#傳入id,拿到請求的返回值 print(res)利用cdp獲取頁面加載時間
這是PyChromeDevTools的官方例子,演示如何獲取頁面加載時間:
import PyChromeDevTools import time import osos.chdir(r"C:\Users\zyj\AppData\Local\Google\Chrome SxS\Application") #這里是改變了當前環境變量 cmd = "chrome.exe --remote-debugging-port=9222" os.popen(cmd)#啟動chrome瀏覽器 chrome = PyChromeDevTools.ChromeInterface() chrome.Network.enable() chrome.Page.enable() start_time=time.time() chrome.Page.navigate(url="http://www.baidu.com/") chrome.wait_event("Page.loadEventFired", timeout=60)#loadEventFired是頁面全部加載完畢的時間,實際上這里還可以用reload方法,選擇去除緩存加載,這樣的時間會更加精確 end_time=time.time()print ("Page Loading Time:", end_time-start_time)利用cdp拿到自動化測試后的js覆蓋率數據并展示
在cdp中,是無法直接得到覆蓋率的數據的,有關js代碼執行情況的統計,在Profiler域,我們可以使用takePreciseCoverage方法來拿到js執行數據,這個數據的數據結構是這樣的:
'result': {'result': [{'scriptId': '17','url':'https://www.xxxxxxxxx.com/browser/guide.js','functions': [{'functionName': 'get','ranges': [{'startOffset': 0,'endOffset': 4273,'count': 1}],'isBlockCoverage': False},}],}],}......一個result包含多個js的統計情況,每個url基本就是js的請求地址;在每個js的統計情況里,又有多個function的統計情況,每個function里的startOffset和endOffset指的是這個方法的被統計語句按字節位置來算的開始位置和結束位置,count代表這段語句是否被執行到,1代表是,0代表否。
因此,思路就是,拿到測試完成后的js統計數據,然后通過每個js統計數據里的每個function的統計坐標值和統計狀態,和原始js數據比對,從而實現對js覆蓋狀況的總覽。
這個實現比較復雜,我直接做成了一個模塊,只需要接受takePreciseCoverage的數據,就可以計算出覆蓋情況并直觀的展示,具體的代碼在github上,這里就不放出了。
最終的效果圖演示:
總結
以上是生活随笔為你收集整理的Chrome remote debugging protocol在自动化测试中的应用和实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS(JavaScript)的初了解3(
- 下一篇: 一、【用django2.0来开发】 环境