Chrome remote debugging protocol在自動化測試中的應用和實踐

從selenium說起

雖然我們的主題是cdp(chrome debug protocol)的應用晒衩,但在介紹cdp之前嗤瞎,不得不先從selenium說起,因為這兩者有密不可分的關系听系。

我們知道贝奇,在最新的selenium里,當你去執(zhí)行一個測試動作靠胜,例如打開瀏覽器弃秆,然后輸入網(wǎng)址,找到一個搜索框填入文本并點擊搜索髓帽,這背后所依賴的技術菠赚,其實是webdriver,而當你的動作執(zhí)行在chrome瀏覽器上郑藏,更為細化的說衡查,依賴的是chromewebdriver。

我們詳細的來分析這一流程必盖,你會更清楚的知道cdp與此有何關系拌牲。

首先我們來寫一個示例代碼:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get("https://www.baidu.com/")

執(zhí)行這段代碼,會看到系統(tǒng)啟動了chrome瀏覽器歌粥,并跳轉到了百度首頁塌忽。

看driver = webdriver.Chrome()這一句,以下是這段代碼的流程圖解失驶。

初始化chrome()的流程圖解.png

可以看到在這個流程中土居,chromedriver起到的是橋梁的作用,他接受客戶端的請求嬉探,然后轉化為瀏覽器的標準指令操作瀏覽器擦耀,而在后半部分,也就是指令如何讓瀏覽器工作中涩堤,就涉及到了我們的主題cdp眷蜓,因為這部分的標準其實就是cdp。

什么是cdp

chrome debug protocol胎围,簡稱cdp吁系。

大家應該都用過chrome瀏覽器的F12,也就是devtools白魂,其實這是一個web應用汽纤,當你使用devtools的時候,瀏覽器本身會作為一個服務端碧聪,而你看到的瀏覽器調試工具界面冒版,其實只是一個前端應用,在這中間通信的逞姿,就是cdp辞嗡,他是基于websocket的捆等,一個讓devtools和瀏覽器內核交換數(shù)據(jù)的通道。

cdp本身是可開放的续室,換句話說栋烤,你用devtools能做什么(例如操作瀏覽器,獲取網(wǎng)絡信息挺狰,獲取js覆蓋數(shù)據(jù)明郭,獲取性能數(shù)據(jù)等等),你就能用cdp做什么丰泊。

cdp的官方文檔地址薯定,可以點擊查閱,這里再簡單的介紹一下瞳购。

cdp把不同的操作劃分為了不同的域(domain)话侄,每個域負責不同的功能模塊,例如学赛,Page域可以獲取當前頁面數(shù)據(jù)年堆,或者操作頁面跳轉等等;Profiler域可以獲取當前的頁面的js覆蓋率數(shù)據(jù)等等盏浇;

直接引用FEX的一篇文章來解釋:

該協(xié)議把操作劃分為不同的域(domain)变丧,比如 DOM、Debugger绢掰、Network痒蓬、Console 和 Timeline 等,可以理解為 DevTools 中的不同功能模塊曼月。

每個域(domain)定義了它所支持的 command 和它所產生的 event谊却。

每個 command 包含 request 和 response 兩部分,request 部分指定所要進行的操作以及操作說要的參數(shù)哑芹,response 部分表明操作狀態(tài),成功或失敗捕透。

command 和 event 中可能涉及到非基本數(shù)據(jù)類型聪姿,在 domain 中被歸為 Type,比如:’frameId’: <FrameId>乙嘀,其中 FrameId 為非基本數(shù)據(jù)類型

至此末购,不難理解:

domain = command + event + type

使用cdp的方式

最原始的使用cdp的方式可以參照google的cdp文檔來:

1.使用附加參數(shù)打開chrome的遠程調試協(xié)議開關(普通模式下的chrome瀏覽器是無法直接使用cdp通信的,另外虎谢,請注意盟榴,在不同的操作系統(tǒng)下指令細節(jié)會有所不同),

chrome.exe --remote-debugging-port = 9222

此時一個打開的遠程調試協(xié)議的瀏覽器實例被啟動婴噩。

2.為做演示擎场,在打開的瀏覽器中羽德,輸入百度的網(wǎng)址并進入,新開一個tab迅办,進入網(wǎng)址http://localhost:9222宅静,此時應該如截圖所示:

點擊標簽.png

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"}}')

執(zhí)行完會發(fā)現(xiàn)矾策,剛才的百度頁面磷账,跳轉到了soso的頁面,其實這段代碼就是新開了一個websocket連接到剛才的百度頁面的調試地址贾虽,然后通過page域的navigate方法讓該頁面重新跳轉到了指定地址逃糟。

需要注意的一點是,在這里榄鉴,每個tab(頁面)都只有一個單獨的通信地址履磨,且每個地址只能與對應的tab通信。

以上就是比較原始的使用方法庆尘,實際上剃诅,cdp有很多封裝好的庫可以使用,例如python的PyChromeDevTools庫驶忌,nodejs的chrome-remote-interface庫等等矛辕,更多上層封裝庫請參見官方文檔

cdp在自動化中的應用和實踐

看了以上內容付魔,可能你會得出一個結論聊品,selenium依賴webdriver,而在chrome瀏覽器中几苍,webdriver又依賴chromedriver翻屈,chromedriver又是依賴cdp的;那么妻坝,我使用selenium和我直接使用cdp伸眶,有什么區(qū)別呢?

實際上真要較真(不怕麻煩)的話刽宪,是沒有區(qū)別的厘贼,但二者還是有一些差異的,selenium的封裝更為上層圣拄,使得你不用去關心原始的cdp到底如何使用嘴秸,而且也集成了聚焦測試所需要的一些功能,例如分布式執(zhí)行,docker image等等岳掐,使得在測試這個需求上凭疮,更為方便;而直接使用cdp的話岩四,會讓整個結構更為簡潔哭尝,而且,有些操作由于webdriver沒有封裝(例如獲取性能數(shù)據(jù)剖煌,獲取js覆蓋率等等)材鹦,所以直接使用cdp會更為精準。那么有沒有辦法讓二者的優(yōu)點結合呢耕姊?

在這里我發(fā)現(xiàn)了兩種方案可以做到桶唐,

1.通過命令行啟動開啟了調試協(xié)議的chrome瀏覽器,然后在selenium里茉兰,初始化webdriver時指定ChromeOption的__debugger_address的值為之前的遠程調試地址尤泽,然后使用selenium操作webdriver,使用PyChromeDevTools操作cdp规脸,示例代碼如下:

import os
import PyChromeDevTools
from selenium import webdriver

cmd = "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 webdriver

driver = webdriver.Chrome()
driver.get("https://www.baidu.com/")
driver.execute_cdp_cmd('Page.navigate',{"url": "http://www.soso.com"})

利用cdp獲取頁面網(wǎng)絡數(shù)據(jù)

有時候當腳本出錯了,我們會希望獲得更多的信息去排查莫鸭,如果這時候能重現(xiàn)當時的網(wǎng)絡請求闹丐,那么排查會容易的多,下面是一個獲取頁面網(wǎng)絡數(shù)據(jù)(response值)的例子被因,這里只拿了請求的response值卿拴,但實際上稍加改動就可以把請求信息拿全(request+response),為了方便演示上面兩種方法梨与,這里混用了上面的兩個方案堕花。

from selenium import webdriver
import time
import os
import PyChromeDevTools

os.chdir(r"C:\Users\zyj\AppData\Local\Google\Chrome SxS\Application") #這里是改變了當前環(huán)境變量
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()#開啟頁面的網(wǎng)絡信息收集模式
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 os

os.chdir(r"C:\Users\zyj\AppData\Local\Google\Chrome SxS\Application") #這里是改變了當前環(huán)境變量
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覆蓋率數(shù)據(jù)并展示

在cdp中,是無法直接得到覆蓋率的數(shù)據(jù)的庄萎,有關js代碼執(zhí)行情況的統(tǒng)計踪少,在Profiler域,我們可以使用takePreciseCoverage方法來拿到js執(zhí)行數(shù)據(jù)糠涛,這個數(shù)據(jù)的數(shù)據(jù)結構是這樣的:

    '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的統(tǒng)計情況援奢,每個url基本就是js的請求地址;在每個js的統(tǒng)計情況里忍捡,又有多個function的統(tǒng)計情況集漾,每個function里的startOffset和endOffset指的是這個方法的被統(tǒng)計語句按字節(jié)位置來算的開始位置和結束位置,count代表這段語句是否被執(zhí)行到砸脊,1代表是具篇,0代表否。

因此凌埂,思路就是驱显,拿到測試完成后的js統(tǒng)計數(shù)據(jù),然后通過每個js統(tǒng)計數(shù)據(jù)里的每個function的統(tǒng)計坐標值和統(tǒng)計狀態(tài)瞳抓,和原始js數(shù)據(jù)比對埃疫,從而實現(xiàn)對js覆蓋狀況的總覽。

這個實現(xiàn)比較復雜孩哑,我直接做成了一個模塊栓霜,只需要接受takePreciseCoverage的數(shù)據(jù),就可以計算出覆蓋情況并直觀的展示臭笆,具體的代碼在github上叙淌,這里就不放出了。

最終的效果圖演示:

覆蓋率效果展示.png

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末愁铺,一起剝皮案震驚了整個濱河市鹰霍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茵乱,老刑警劉巖茂洒,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓶竭,居然都是意外死亡督勺,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門斤贰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來智哀,“玉大人,你說我怎么就攤上這事荧恍〈山校” “怎么了屯吊?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摹菠。 經常有香客問我盒卸,道長,這世上最難降的妖魔是什么次氨? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任蔽介,我火速辦了婚禮,結果婚禮上煮寡,老公的妹妹穿的比我還像新娘虹蓄。我一直安慰自己,他們只是感情好洲押,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布武花。 她就那樣靜靜地躺著,像睡著了一般杈帐。 火紅的嫁衣襯著肌膚如雪体箕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天挑童,我揣著相機與錄音累铅,去河邊找鬼。 笑死站叼,一個胖子當著我的面吹牛娃兽,可吹牛的內容都是我干的。 我是一名探鬼主播尽楔,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼投储,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了阔馋?” 一聲冷哼從身側響起玛荞,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呕寝,沒想到半個月后勋眯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡下梢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年客蹋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孽江。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡讶坯,死狀恐怖,靈堂內的尸體忽然破棺而出岗屏,到底是詐尸還是另有隱情闽巩,我是刑警寧澤钧舌,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站涎跨,受9級特大地震影響,放射性物質發(fā)生泄漏崭歧。R本人自食惡果不足惜隅很,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望率碾。 院中可真熱鬧叔营,春花似錦、人聲如沸所宰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽仔粥。三九已至婴谱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間躯泰,已是汗流浹背谭羔。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留麦向,地道東北人瘟裸。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像诵竭,于是被迫代替她去往敵國和親话告。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內容