利用Chrome開發者工具進行WebView的測試(初級版本)

前言

繼上篇文章騰訊FAutoTest解讀源碼寫出來之後
這篇文章則是將其中的精華抽出,並引導你如何使用簡短的代碼,實現FAT的功能。

我們這次用雪球股票這個App當我們的測試項目,直接使用Xposed強行開啓項目的WebView Debug模式,如果測試自己的App,並且有開發在旁邊也可以讓他/她加句法代碼

if (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)) {
    WebView.setWebContentsDebuggingEnabled(true);
}
在我之前寫過的文章Android WebView研究筆記有提到過Xposed這個應用

手動實現WebView自動化

:App,點擊實盤交易就進入了WebView接口,WebView接口上的所有內容都不是原生控件了。

使用下面的命令找出雪球的pid

$ adb shell ps | grep xueqiu
u0_a133   4433  566   1634988 264956 SyS_epoll_ 0000000000 S com.xueqiu.android
u0_a133   4510  566   1208020 78048 SyS_epoll_ 0000000000 S com.xueqiu.android:pushservice

然後再執行個命令

$ adb shell cat /proc/net/unix | grep --binary-file=text webview
0000000000000000: 00000002 00000000 00010000 0001 01 1433219 @webview_devtools_remote_4433

這裏看到的Android了監聽@webview_devtools_remote_4433其中的4433對應的是雪球的PID(注:名字可能不會這麼規範,可能是。webview_devtools_remote_m6x_4433,也可能是browser_webview_devtools_remote_22091
我們接着用亞行向前命令將網頁視圖調試轉發到本地。

$ adb forward tcp:5000 localabstract:webview_devtools_remote_4433
$ curl localhost:5000/json/version
{
   "Android-Package": "com.xueqiu.android",
   "Browser": "Chrome/62.0.3202.84",
   "Protocol-Version": "1.2",
   "User-Agent": "Mozilla/5.0 (Linux; Android 6.0.1; SM901 Build/MXB48T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36",
   "V8-Version": "6.2.414.37",
   "WebKit-Version": "537.36 (@957d898f0f6e46cd9661d91d2bae899f34c1c4b6)",
   "webSocketDebuggerUrl": "ws://localhost:5000/devtools/browser"
}

通過此信息我們也可以確定當前連接的webview devtools確實是雪球的。
接下來訪問localhost:5000/json/list該地址,可以獲取到webview所有打開的界面。(訪問localhost:5000 / json也可以)

webview就是一個簡化修改版的瀏覽器。每一個頁面對應一個標籤。
$ curl localhost:5000/json/list
[ {
   "description": "{\"attached\":true,\"empty\":false,\"height\":1698,\"screenX\":0,\"screenY\":222,\"visible\":true,\"width\":1080}",
   "devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@957d898f0f6e46cd9661d91d2bae899f34c1c4b6/inspector.html?ws=localhost:5000/devtools/page/2c1c9bfc-e941-42a9-bfc1-0b1f2f73e553",
   "id": "2c1c9bfc-e941-42a9-bfc1-0b1f2f73e553",
   "title": "實盤交易",
   "type": "page",
   "url": "file:///data/user/0/io.va.exposed/virtual/data/user/0/com.xueqiu.android/files/com.xueqiu.android.h5/modules/broker/tradeHome.html",
   "webSocketDebuggerUrl": "ws://localhost:5000/devtools/page/2c1c9bfc-e941-42a9-bfc1-0b1f2f73e553"
} ]

因爲Chrome DevTools Protocal(CDP)是基於WebSocket協議的,我用門python websocket_client庫來進行下交互。

import websocket
import json
from pprint import pprint

conn = websocket.create_connect("ws://localhost:5000/devtools/page/2c1c9bfc-e941-42a9-bfc1-0b1f2f73e553")
conn.write(json.dumps({"id": 1, "method": "Runtime.evaluate", "params": {"expression": "window.location.toString()"}}))
data = conn.recv()
pprint(data)
# 輸出:{"id":1,"result":{"result":{"type":"string","value":"https://-- 一個很長的網址--"}}}

發送的消息格式跟jsonrpc 2.0協議幾乎一模一樣。
Github的上搜了搜,發現有專門針對鉻Devtools協議的寫庫https://github.com/fate0/pychrome,下面的代碼用pychome庫重寫一下,並加入XPath獲取元素的功能

JS如何使用XPath參考了:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_using_XPath_in_JavaScript
import pychrome
import json

browser = pychrome.Browser("http://localhost:5000")
tab = None
for t in browser.list_tab():
    t.start()
    hidden = t.call_method("Runtime.evaluate", expression="document.hidden")["result"]["value"]
    if not hidden: # 頁面處於激活狀態
        tab = t
        break
    t.stop()
else:
    raise RuntimeError("no activated tab")

# 獲取 按鈕(天氣)的中心座標
ret = tab.call_method("Runtime.evaluate", expression="""
(function(){
    var xpath = '//*[contains(text(), "天氣")]'
    var obj = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);
    var button = obj.iterateNext();
    var rect = button.getBoundingClientRect()
    // [rect.left, rect.top, rect.right, rect.bottom]
    var x = (rect.left + rect.right)/2
    var y = (rect.top + rect.bottom)/2;
    return JSON.stringify([x, y])
})
"""

x, y = json.loads(ret['result']['value'])

# 執行點擊操作
tab.Input.synthesizeTapGesture(x=x, y=y, duration=200, tapCount=1)
# 等價於 tab.call_method("Input.synthesizeTapGesture", x=x, y=y, duration=200, tapCount=1)

tab.stop()
tab.wait(5) # 等待thread進程退出

到這個地方,我們就實現了通過XPath查找對應空間,然後單擊的功能。繼續擴展下,說不定能夠弄出一個webview測試框架出來。

參考資料

作者:codeskyblue

原文鏈接:https://testerhome.com/topics/19461

轉載文章時務必註明原作者及原始鏈接,並註明「發表於 TesterHome 」,並不得對作品進行修改。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章