Software testing/test development丨Appium WebView technical principle of app automation testing

Public account search: TestingStudio Hogwarts test development dry goods are very hardcore

Hybrid application testing or WeChat applet testing will involve the WebView component. This section will analyze the technical principles of WebView. First, check the running process of Appium through log analysis.

WebView log analysis

To view the log of ChromeDriver, you need to enable a switch item showChromedriverLog in Capability. Let Appium generate ChromeDriver-related logs when running test cases (by default, this part of the log for ChromeDriver is not printed). code show as below:

capabilities['showChromedriverLog'] = True

Start AppiumServer and store all generated logs into the file /tmp/appium.log:

appium -g /tmp/appium.log

Key log analysis

Below we analyze the key logs in the /tmp/appium.log file in detail. First find the log of Context switching, and find that Context is switched to WEBVIEW_io.appium.android.apis context:

[HTTP] <-- GET /wd/hub/session/xx/contexts 200 99 ms - 145
[HTTP] --> POST /wd/hub/session/xx/context \
{<!-- -->"name":"WEBVIEW_io.appium.android.apis"}

Appium has opened two WebView processes locally, the process numbers are 1271 and 26060 respectively.

[debug] [AndroidDriver] WEBVIEW_1271 mapped to pid 1271
[debug] [AndroidDriver] Getting process name for webview
[debug] [ADB] Getting connected devices...
[debug] [AndroidDriver] WEBVIEW_26060 mapped to pid 26060
[debug] [AndroidDriver] Getting process name for webview

Then, Appium can view the process information of 1271 and 26060 by using the adb command.

[debug] [ADB] Running '/Users/xxxx/profile/android-sdk_r2.4.1-\
macosx/android-sdk-macosx/platform-tools/adb' with args: ["-P",5037,\
"-s","192.168.56.101:5555","shell","ps"]
[debug] [AndroidDriver] Parsed pid: '1271' pkg: 'cn.goapk.market' from
[debug] [AndroidDriver] USER PID PPID VSIZE RSS \
WCHAN\ PC NAME
[debug] [AndroidDriver] u0_a58 1271 192 606848 53268 \
ffffffff b76c707b S cn.goapk.market
[debug] [AndroidDriver] Returning process name: 'cn.goapk.market'
[debug] [AndroidDriver] Parsed pid: '26060' pkg: 'io.appium.android.\
apis'\ from
[debug] [AndroidDriver] USER PID PPID VSIZE RSS \
  WCHAN PC NAME
[debug] [AndroidDriver] u0_a139 26060 192 649076 68004\
 ffffffff b76c6371 R io.appium.android.apis
[debug] [AndroidDriver] Returning process name:\
 'io.appium.android.apis'

Appium Server lists all WebViews and available Contexts found through the process, as follows:

[debug] [AndroidDriver] Found webviews: ["WEBVIEW_cn.goapk.market",\
"WEBVIEW_io.appium.android.apis"]
[debug] [AndroidDriver] Available contexts: ["NATIVE_APP",\
"WEBVIEW_cn.goapk.market","WEBVIEW_io.appium.android.apis"]

Then Appium Server tries to connect to ChromeDriver. Since we did not set the port of ChromeDriver, Appium Server enables port 8000 to communicate with WebView by default. Appium Server communicates with app WebView through ChromeDriver.

[debug] [AndroidDriver] Connecting to chrome-backed webview context\
 'WEBVIEW_io.appium.android.apis'
[debug] [AndroidDriver] A port was not given, using random port: 8000

Then, Appium first kills the ChromeDriver process, and cleans up the adb mapping port data, as follows:

[debug] [Chromedriver] Killing any old chromedrivers, running: \
pkill -15 -f "/usr/local/lib/node_modules/appium/node_modules/\
[email protected]@appium-chromedriver/chromedriver/mac/\
chromedriver.*--port=8000"
[Chromedriver] No old chromedrivers seemed to exist
[debug] [Chromedriver] Cleaning any old adb forwarded port socket \
connections
[debug] [ADB] List forwarding ports
[debug] [ADB] Running '/Users/xxx/profile/android-sdk_r24.4.1-macosx\
/android-sdk-macosx/platform-tools/adb' with args: ["-P",5037,"-s","192.168.56.101:5555","forward", "--list"]

At this point, the ChromeDriver service is actually started, and the communication between Appium Server and ChromeDriver is established.

[Chromedriver] Spawning chromedriver with: /usr/local/lib/\
node_modules/appium/node_modules/[email protected]@\
appium-chromedriver/chromedriver/mac/chromedriver --url-base=wd/hub \
--port=8000 --adb-port=5037 --verbose

After establishing a connection, Appium Server first checks the ChromeDriver version number.

[debug] [Chromedriver] Chromedriver version: '2.33.506106'

ChromeDriver prints the startup log and starts transferring parameters.

[debug] [Chromedriver] [STDOUT] Starting ChromeDriver 2.33.506106 \
(8a06c39c4582fbfbab6966dbb1c38a9173bfb1a2) on port 8000
[debug] [Chromedriver] [STDOUT] Only local connections are allowed.
[debug] [JSONWP Proxy] Proxying [GET /status] to [GET\
 http://127.0.0.1:8000/wd/hub/status] with no body
[debug] [JSONWP Proxy] Got response with status 200:\
 "{<!-- -->"sessionId":"","status":0,"value" :{<!-- -->"build":{<!-- -->"version
 ":"alpha"},"os":{<!-- -->"arch":"x86_64\ ","name":"Mac OS X",
 "version":"10.12.6"}}}"
[debug] [JSONWP Proxy] Proxying [POST /session] to [POST \
http://127.0.0.1:8000/wd/hub/session] with body: {<!-- -->"\
desiredCapabilities"\:{<!-- -->"chromeOptions":{<!-- -->"androidPackage":\
"io.appium.android.apis","androidUseRunningApp":true,"androidDeviceSerial":"192.168.56.101:5555"}}}

Use the ps command to list the process list of the mobile phone and find WebView from it.

[Chromedriver] [STDERR] [0.349][DEBUG]: \
Sending adb command: \
host:transport:192.168.56.101:5555|shell:ps & amp; & amp; ps -A

Obtain the process ID of WebView through the process list, and Appium operates WebView based on this information.

Chromedriver] [STDERR] [0.365][DEBUG]: \
Sending adb command: \
host-serial:192.168.56.101:5555:forward:tcp:12531;\
localabstract:webview_devtools_remote_26060

View WebView

The WebView control will be mapped as a native control, the type is View, and the text content in it will become content-desc (Android 6.0) or text (Android 9.0).

Open the Snowball APP, the following command can view the WebView process in the Android system:

Hogwarts $ adb shell cat /proc/net/unix | grep webview
0000000000000000: 00000002 00000000 00010000 0001 01 12863 /dev/socket/webview_zygote
0000000000000000: 00000002 00000000 00010000 0001 01 24703 @webview_devtools_remote_1758
0000000000000000: 00000003 00000000 00000000 0001 03 24672 /dev/socket/webview_zygote

In the above command, all names preceded by @ are sockets. A socket is an abstraction of an endpoint for two-way communication between application processes on different hosts in a network.

The following command checks the application corresponding to this process:

Hogwarts $ adb shell ps |grep 1758

u0_a67 1758 211 1600592 333680 futex_wait_queue_me f016fbb9 S com.xueqiu.android

It is the WebView process of Xueqiu APP. During testing, Appium Server uses this port to communicate with WebView.

Context switching

Since manipulating a socket directly is difficult, use adb forward to redirect it to a local port.

adb forward tcp:$port localabstract:webview_devtools_remote_$pid

Example: use local port 7777

Hogwarts $ adb forward tcp:7777 localabstract:webview_devtools_remote_1758

You can send an http request to implement related operations, and get the version of the WebView component below (you can also directly access http://localhost:7777/json/version in the browser), the command is as follows:

Hogwarts $ curl http://localhost:7777/json/version
{<!-- -->
   "Android-Package": "com.xueqiu.android",
   "Browser": "Chrome/74.0.3729.186",
   "Protocol-Version": "1.3",
   "User-Agent": "Mozilla/5.0 (Linux; Android 8.1.0; Google Pixel_1 Build/OPM6.171019.030.E1; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.186 Mobile Safari/537.36",
   "V8-Version": "7.4.288.28",
   "WebKit-Version": "537.36 (@99fc61ba7ee9c511608e5ea11edc2622ba3b8e3f)",
   "webSocketDebuggerUrl": "ws://localhost:7777/devtools/browser"
}

Chrome’s devtools protocol is a remote debugging protocol (https://chromedevtools.github.io/devtools-protocol/)

http://127.0.0.1:7777/json/list You can directly get devtoolsFrontendUrl to view the page details of the debugged remote redirection.

[
    {<!-- -->
        "description": "{<!-- -->"attached":true,"empty":false,"height": 1605,"screenX":0,"screenY":189,"visible":true,"width":1080} ",
        "devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@99fc61ba7ee9c511608e5ea11edc2622ba3b8e3f/inspector.html?ws=127.0.0.1:7777/devtools/page/06840BED58C1415235EFC9817FFD472E",
        "faviconUrl": "https://assets.imedao.com/broker/static/images/favicon.8d2e0522.png",
        "id": "06840BED58C1415235EFC9817FFD472E",
        "title": "Ping An Securities Quick Account Opening",
        "type": "page",
        "url": "https://broker.xueqiu.com/open/pingan?snb_from=tab",
        "webSocketDebuggerUrl": "ws://127.0.0.1:7777/devtools/page/06840BED58C1415235EFC9817FFD472E"
    },
    ...

Then visit Chrome’s debugging page chrome://inspect/#devices, you can get the page element information, so as to complete the positioning of the element.

image

image