天天看點

快速搞定 uiautomator2 自動化測試工具使用

快速搞定 uiautomator2 自動化測試工具使用

霍格沃茲測試學院是 python-uiautomator2 金牌贊助商,跟着開源項目作者學測試開發實戰。

一、背景簡介

Google 官方提供了一個 Android 自動化測試工具(Java 庫),基于 Accessibility 服務,功能很強,可以對第三方 App 進行測試,擷取螢幕上任意一個 App 的任意一個控件屬性,并對其進行任意操作,但有兩個缺點:

  1. 測試腳本隻能使用 Java 語言;
  2. 測試腳本要打包成 jar 或者 apk 包上傳到裝置上才能運作;

實際工作中,我們希望測試邏輯能夠用 Python 編寫,能夠在電腦上運作的時候就控制手機。是以基于這個目的開發了 python-uiautomator2 自動化測試開源工具,其封裝了谷歌自帶的 uiautomator2 測試架構,可以運作在支援 Python 的任一系統上,目前版本為 V2.10.2。

GitHub 開源位址:

https://github.com/openatx/uiautomator2

==二、工作原理==

快速搞定 uiautomator2 自動化測試工具使用

如圖所示,python-uiautomator2 主要分為兩個部分,python 用戶端,移動裝置

  • python 端: 運作腳本,并向移動裝置發送 HTTP 請求;
  • 移動裝置:移動裝置上運作了封裝了 uiautomator2 的 HTTP 服務,解析收到的請求,并轉化成 uiautomator2 的代碼;

整個過程:

  1. 在移動裝置上安裝

    atx-agent

    (守護程序),随後

    atx-agent

    啟動

    uiautomator2

    服務(預設 7912 端口)進行監聽;
  2. 在 PC 上編寫測試腳本并執行(相當于發送 HTTP 請求到移動裝置的 server 端);
  3. 移動裝置通過 WIFI 或 USB 接收到 PC 上發來的 HTTP 請求,執行制定的操作;

三、安裝與啟動

3.1 安裝 uiautomator2

使用 pip 安裝

pip install -U uiautomator2           

安裝完成後,使用如下 python 代碼檢視環境是事配置成功

說明:後文中所有代碼都需要導入 uiautomator2 庫,為了簡化我使用 u2 代替,d 代表 driver

import uiautomator2 as u2
# 連接配接并啟動
d = u2.connect() 
print(d.info)           

能正确列印出裝置的資訊則表示安裝成功

快速搞定 uiautomator2 自動化測試工具使用

注意:需要安裝 adb 工具,并配置到系統環境變量,才能操作手機。

安裝有問題可以到 issue 清單查詢:

https://github.com/openatx/uiautomator2/wiki/Common-issues

3.2 安裝 weditor

weditor 是一款基于浏覽器的 UI 檢視器,用來幫助我們檢視 UI 元素定位。

因為 uiautomator 是獨占資源,是以當 atx 運作的時候 uiautomatorviewer 是不能用的,為了減少 atx 頻繁的啟停,就需要用到此工具

pip install -U weditor           

檢視安裝是否成功

weditor --help           

出現如下資訊表示安裝成功

快速搞定 uiautomator2 自動化測試工具使用

運作 weditor

python -m weditor
#或者直接在指令行運作
weditor           

四、元素定位

4.1 使用方法

d(定位方式 = 定位值)
#例:
element = d(text='Phone')
#這裡傳回的是一個清單,當沒找到元素時,不會報錯,隻會傳回一個長度為 0 的清單
#當找到多個元素時,會傳回多個元素的清單,需要加下标再定位
element[0].click()
#擷取元素個數
print(element.count)           

4.2 支援的定位方式

ui2 支援 android 中 UiSelector 類中的所有定位方式,詳細可以在這個網址檢視

https://developer.android.com/reference/android/support/test/uiautomator/UiSelector

整體内容如下 , 所有的屬性可以通過 weditor 檢視到

名稱 描述
text text 是指定文本的元素
textContains text 中包含有指定文本的元素
textMatches text 符合指定正則的元素
textStartsWith text 以指定文本開頭的元素
className className 是指定類名的元素
classNameMatches className 類名符合指定正則的元素
description description 是指定文本的元素
descriptionContains description 中包含有指定文本的元素
descriptionMatches description 符合指定正則的元素
descriptionStartsWith description 以指定文本開頭的元素
checkable 可檢查的元素,參數為 True,False
checked 已選中的元素,通常用于複選框,參數為 True,False
clickable 可點選的元素,參數為 True,False
longClickable 可長按的元素,參數為 True,False
scrollable 可滾動的元素,參數為 True,False
enabled 已激活的元素,參數為 True,False
focusable 可聚焦的元素,參數為 True,False
focused 獲得了焦點的元素,參數為 True,False
selected 目前選中的元素,參數為 True,False
packageName packageName 為指定包名的元素
packageNameMatches packageName 為符合正則的元素
resourceId resourceId 為指定内容的元素
resourceIdMatches resourceId 為符合指定正則的元素

4.3 子元素和兄弟定位

子元素定位

child()

#查找類名為 android.widget.ListView 下的 Bluetooth 元素
d(className="android.widget.ListView").child(text="Bluetooth")
# 下面這兩種方式定位有點不準确,不建議使用
d(className="android.widget.ListView")\
.child_by_text("Bluetooth",allow_scroll_search=True)
d(className="android.widget.ListView").child_by_description("Bluetooth")           

兄弟元素定位

sibling()

#查找與 google 同一級别,類名為 android.widget.ImageView 的元素
d(text="Google").sibling(className="android.widget.ImageView")           

鍊式調用

d(className="android.widget.ListView", resourceId="android:id/list") \
  .child_by_text("Wi‑Fi", className="android.widget.LinearLayout") \
  .child(className="android.widget.Switch") \
  .click()           

4.4 相對定位

相對定位支援在

left

,

right

top

bottom

, 即在某個元素的前後左右

d(A).left(B),# 選擇 A 左邊的 B
d(A).right(B),# 選擇 A 右邊的 B
d(A).up(B), #選擇 A 上邊的 B
d(A).down(B),# 選擇 A 下邊的 B
#選擇 WIFI 右邊的開關按鈕
d(text='Wi‑Fi').right(resourceId='android:id/widget_frame')           

4.5 元素常用 API

表格标注有 @property 裝飾的類屬性方法,均為下方示例方式

d(test="Settings").exists           
方法 傳回值 備注
exists() 判斷元素是否存在 True,Flase @property
info() 傳回元素的所有資訊 字典
get_text() 傳回元素文本 字元串
set_text(text) 設定元素文本 None
clear_text() 清空元素文本 None
center() 傳回元素的中心點位置 (x,y) 基于整個螢幕的點

exists 其它使用方法:

d.exists(text='Wi‑Fi',timeout=5)           

info() 輸出資訊:

{
  "bounds": {
    "bottom": 407,
    "left": 216,
    "right": 323,
    "top": 342
  },
  "childCount": 0,
  "className": "android.widget.TextView",
  "contentDescription": null,
  "packageName": "com.android.settings",
  "resourceName": "android:id/title",
  "text": "Wi‑Fi",
  "visibleBounds": {
    "bottom": 407,
    "left": 216,
    "right": 323,
    "top": 342
  },
  "checkable": false,
  "checked": false,
  "clickable": false,
  "enabled": true,
  "focusable": false,
  "focused": false,
  "longClickable": false,
  "scrollable": false,
  "selected": false
}           

可以通過上方資訊分别擷取元素的所有屬性

4.6 XPATH 定位

因為 Java uiautoamtor 中預設是不支援 xpath,這是屬于 ui2 的擴充功能,速度會相比其它定位方式慢一些

在 xpath 定位中,ui2 中的 description 定位需要替換為 content-desc,resourceId 需要替換為 resource-id

使用方法

# 隻會傳回一個元素,如果找不到元素,則會報 XPathElementNotFoundError 錯誤
# 如果找到多個元素,預設會傳回第 0 個
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]')

# 如果傳回的元素有多個,需要使用 all() 方法傳回清單
# 使用 all 方法,當未找到元素時,不會報錯,會傳回一個空清單
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]').all()           

五、裝置互動

5.1 單擊

d(text='Settings').click()
#單擊直到元素消失 , 逾時時間 10,點選間隔 1
d(text='Settings').click_gone(maxretry=10, interval=1.0)           

5.2 長按

d(text='Settings').long_click()           

5.3 拖動

Android<4.3 時不能使用拖動

# 在 0.25S 内将 Setting 拖動至 Clock 上,拖動元素的中心位置
# duration 預設為 0.5, 實際拖動的時間會比設定的要高
d(text="Settings").drag_to(text="Clock", duration=0.25)

# 拖動 settings 到螢幕的某個點上
d(text="Settings").drag_to(877,733, duration=0.25)

#兩個點之間的拖動 , 從點 1 拖動至點 2
d.drag(x1,y1,x2,y2)           

5.4 滑動

滑動有兩個,一個是在 driver 上操作,一個是在元素上操作

元素上操作

從元素的中心向元素邊緣滑動

# 在 Setings 上向上滑動。steps 預設為 10
# 1 步約為 5 毫秒,是以 20 步約為 0.1 s
d(text="Settings").swipe("up", steps=20)            

driver 上操作

即對整個螢幕操作

# 實作下滑操作
x,y = d.window_size()
x1 = x / 2
y1 = y * 0.1
y2 = y * 0.9
d.swipe(x1,y1,x1,y2)           

driver 滑動的擴充方法,可以直接實作滑動,不需要再自己封裝定位點

# 支援前後左右的滑動
# "left", "right", "up", "down"
# 下滑操作
d.swipe_ext("down")           

5.5 雙指操作

android>4.3

對元素操作

d(text='Settings').gesture(start1,start2,end1,end2,)
# 放大操作
d(text='Settings').gesture((525,960),(613,1121),(135,622),(882,1540))           

封裝好的放大縮小操作

# 縮小
d(text="Settings").pinch_in()
# 放大
d(text="Settings").pinch_out()           

5.6 等待元素出現或者消失

# 等待元素出現
d(text="Settings").wait(timeout=3.0)
# 等待元素消失,傳回 True False,timout 預設為全局設定的等待時間
d(text='Settings').wait_gone(timeout=20)           

5.7 滾動界面

設定 scrollable 屬性為 True;

滾動類型:horiz 為水準,vert 為垂直;

滾動方向:

  • forward 向前
  • backward 向後
  • toBeginning 滾動至開始
  • toEnd 滾動至最後
  • to 滾動直接某個元素出現

所有方法均傳回 Bool 值;

# 垂直滾動到頁面頂部 / 橫向滾動到最左側
d(scrollable=True).scroll.toBeginning()
d(scrollable=True).scroll.horiz.toBeginning()
# 垂直滾動到頁面最底部 / 橫向滾動到最右側
d(scrollable=True).scroll.toEnd()
d(scrollable=True).scroll.horiz.toEnd()
# 垂直向後滾動到指定位置 / 橫向向右滾動到指定位置
d(scrollable=True).scroll.to(description=" 指定位置 ")
d(scrollable=True).scroll.horiz.to(description=" 指定位置 ")
# 垂直向前滾動(橫向同理)
d(scrollable=True).scroll.forward()
# 垂直向前滾動到指定位置(橫向同理)
d(scrollable=True).scroll.forward.to(description=" 指定位置 ")
# 滾動直到 System 元素出現
d(scrollable=True).scroll.to(text="System")
Take screenshot of widget

im = d(text="Settings").screenshot()
im.save("settings.jpg")           

5.8 輸入

5.8.1 輸入自定義文本

# 使用 adb 廣播的方式輸入
d.send_keys('hello')
# 清空輸入框
d.clear_text()           

5.8.2 輸入按鍵

兩種方法

# 發送回車
d.press('enter')
# 第二種
d.keyevent('enter')           

目前 press 支援的按鍵如下

"""
        press key via name or key code. Supported key name includes:
            home, back, left, right, up, down, center, menu, search, enter,
            delete(or del), recent(recent apps), volume_up, volume_down,
            volume_mute, camera, power.
        """           

keyevent 是通過 “adb shell input keyevent” 方式輸入,支援按鍵更加豐富

更多詳細的按鍵資訊

https://developer.android.com/reference/android/view/KeyEvent.html

5.8.3 輸入法切換

# 切換成 ui2 的輸入法,這裡會隐藏掉系統原本的輸入法 , 預設是使用系統輸入法
# 當傳入 False 時會使用系統預設輸入法,預設為 Fasle
d.set_fastinput_ime(True)
# 檢視目前輸入法
d.current_ime()
#傳回值
('com.github.uiautomator/.FastInputIME', True)           

5.8.4 模拟輸入法功能

可以模拟的功能有 go ,search ,send ,next, done ,previous。

如果使用 press 輸入按鍵無效,可以嘗試使用此方法輸入

# 搜尋功能
d.send_action("search")           

5.9 toast 操作

# 擷取 toast, 當沒有找到 toast 消息時,傳回 default 内容
d.toast.get_message(timout=5,default='no toast')
# 清空 toast 緩存
d.toast.reset()           

5.10 監控界面

使用 wather 進行界面的監控,可以用來實作跳過測試過程中的彈框

當啟動 wather 時,會建立一個線程進行監控

可以添加多個 watcher

用法

# 注冊監控 , 當界面内出現有 allow 字樣時,點選 allow
d.watcher.when('allow').click()

# 移除 allow 的監控
d.watcher.remove("allow")

# 移除所有的監控
d.watcher.remove()

# 開始背景監控
d.watcher.start()
d.watcher.start(2.0) # 預設監控間隔 2.0s

# 強制運作所有監控
d.watcher.run()

# 停止監控
d.watcher.stop()

# 停止并移除所有的監控,常用于初始化
d.watcher.reset()           

2.11.0 版本 新增了一個 watch_context 方法 , 寫法相比 watcher 更簡潔,官方推薦使用此方法來實作監控,目前隻支援 click() 這一種方法。

wct = d.watch_context()
# 監控 ALLOW
wct.when("ALLOW").click()
# 監控 OK
wct.when('OK').click()
 # 開啟彈窗監控,并等待界面穩定(兩個彈窗檢查周期内沒有彈窗代表穩定)
wct.wait_stable()

#其它實作代碼
# 停止監控
wct.stop()           

5.11 多點滑動

這裡可以用來實作圖案解鎖

使用 touch 類

# 模拟按下不放手
touch.down(x,y)
# 停住 3S
touch.sleep(x,y)
# 模拟移動
touch.move(x,y)
# 模拟放開
touch.up(x,y)
#實作長按 , 同一個點按下休眠 5S 後擡起
d.touch.down(252,1151).sleep(5).up(252,1151)
# 實作四點的圖案解鎖,目前隻支援坐标點
d.touch.down(252,1151).move(559,1431).move(804,1674).move(558,1666).up(558,1666)           

六、圖像操作

6.1 截圖

d.screenshot('test.png')           

6.2 錄制視訊

這個感覺是比較有用的一個功能,可以在測試用例開始時錄制,結束時停止錄制,然後如果測試 fail。則上傳到測試報告,完美複原操作現場,具體原理後面再去研究。

首先需要下載下傳依賴,官方推薦使用鏡像下載下傳:

pip3 install -U "uiautomator2[image]" -i https://pypi.doubanio.com/simple           

執行錄制:

# 啟動錄制,預設幀率為 20
d.screenrecord('test.mp4')
# 其它操作
time.sleep(10)
#停止錄制,隻有停止錄制了才能看到視訊 
d.screenrecord.stop()           

6.3 圖檔識别點選

下載下傳與錄制視訊同一套依賴。

這個功能是首先手動截取需要點選目标的圖檔,然後 ui2 在界面中去比對這個圖檔,目前我嘗試了精确試不是很高,誤點率非常高,不建議使用。

# 點選 
d.image.click('test.png')
# 比對圖檔,傳回相似度和坐标
# {'similarity': 0.9314796328544617, 'point': [99, 630]}
d.image.match('test.png')           

七、應用管理

7.1 擷取目前界面的 APP 資訊

d.app_current()
#傳回目前界面的包名,activity 及 pid
{
    "package": "com.xueqiu.android",
    "activity": ".common.MainActivity",
    "pid": 23007
}           

7.2 安裝應用

可以從本地路徑及 url 下載下傳安裝 APP,此方法無傳回值,當安裝失敗時,會抛出 RuntimeError 異常

# 本地路徑安裝 
d.app_install('test.apk')
# url 安裝 
d.app_install('http://s.toutiao.com/UsMYE/')           

7.3 運作應用

預設當應用在運作狀态執行 start 時不會關閉應用,而是繼續保持目前界面。

如果需要消除前面的啟動狀态,則需要加 stop=True 參數。

# 通過包名啟動
d.app_start("com.xueqiu.android",stop=True)

#源碼說明
    def app_start(self, package_name: str, 
                  activity: Optional[str]=None, 
                  wait: bool = False, 
                  stop: bool=False, 
                  use_monkey: bool=False):
        """ Launch application
        Args:
            package_name (str): package name
            activity (str): app activity
            stop (bool): Stop app before starting the activity. (require activity)
            use_monkey (bool): use monkey command to start app when activity is not given
            wait (bool): wait until app started. default False
        """           

7.4 停止應用

stop 和 clear 的差別是結束應用使用的指令不同

stop 使用的是 “am force-stop”

clear 使用的是 “pm clear”

# 通過包名結束單個應用
d.app_stop("com.xueqiu.android")
d.app_clear('com.xueqiu.android')

# 結束所有應用 , 除了 excludes 參數清單中的應用包名
# 如果不傳參,則會隻保留兩個依賴服務應用
# 會傳回一個結束應用的包名清單
d.app_stop_all(excludes=['com.xueqiu.android'])           

7.5 擷取應用資訊

d.app_info('com.xueqiu.android')

#輸出
{
    "packageName": "com.xueqiu.android",
    "mainActivity": "com.xueqiu.android.common.splash.SplashActivity",
    "label": " 雪球 ",
    "versionName": "12.6.1",
    "versionCode": 257,
    "size": 72597243
}           

7.6 擷取應用圖示

img = d.app_icon('com.xueqiu.android')
img.save('icon.png')           

7.7 等待應用啟動

# 等待此應用變為目前應用,傳回 pid,逾時未啟動成功則傳回 0
# front 為 true 表示等待 app 成為目前 app,
# 預設為 false,表示隻要背景有這個應用的程序就會傳回 PID
d.app_wait('com.xueqiu.android',60,front=True)           

7.8 解除安裝應用

# 解除安裝成功傳回 true, 沒有此包或者解除安裝失敗傳回 False
d.app_uninstall('com.xueqiu.android')

# 解除安裝所有自己安裝的第三方應用 , 傳回解除安裝 app 的包名清單
# excludes 表示不解除安裝的清單
# verbose 為 true 則會列印解除安裝資訊
d.app_uninstall_all(excludes=[],verbose=True)           

解除安裝全部應用傳回的包名清單并一定是解除安裝成功了,最好使用 verbose=true 列印一下資訊,這樣可以檢視到是否解除安裝成功

uninstalling com.xueqiu.android  OK
uninstalling com.android.cts.verifier  FAIL           

或者可以修改一下源碼,使其隻輸出成功的包名,注釋的為增加的代碼,未注釋的是源碼

def app_uninstall_all(self, excludes=[], verbose=False):
        """ Uninstall all apps """
        our_apps = ['com.github.uiautomator', 'com.github.uiautomator.test']
        output, _ = self.shell(['pm', 'list', 'packages', '-3'])
        pkgs = re.findall(r'package:([^\s]+)', output)
        pkgs = set(pkgs).difference(our_apps + excludes)
        pkgs = list(pkgs)
        # 增加一個解除安裝成功的清單
        #sucess_list = []
        for pkg_name in pkgs:
            if verbose:
                print("uninstalling", pkg_name, " ", end="", flush=True)
            ok = self.app_uninstall(pkg_name)
            if verbose:
                print("OK" if ok else "FAIL")
                # 增加如下語句,當成功則将包名加入 list
                #if ok:
                 #   sucess_list.append(pkg_name)
     # 傳回成功的清單
    #    return sucess_list
        return pkgs           

八、其它實用方法

8.1 連接配接裝置

#當 PC 隻連接配接了一個裝置時,可以使用此種方式
d = u2.connect()
#傳回的是 Device 類 , 此類繼承方式如下

class Device(_Device, _AppMixIn, _PluginMixIn, _InputMethodMixIn, _DeprecatedMixIn):
    """ Device object """


# for compatible with old code
Session = Device           

connect() 可以使用如下其它方式進行連接配接

#當 PC 與裝置在同一網段時,可以使用 IP 位址和端口号通過 WIFI 連接配接,無需連接配接 USB 線
connect("10.0.0.1:7912")
connect("10.0.0.1") # use default 7912 port
connect("http://10.0.0.1")
connect("http://10.0.0.1:7912")
#多個裝置時,使用裝置号指定哪一個裝置
connect("cff1123ea")  # adb device serial number           

8.2 擷取裝置及 driver 資訊

8.2.1 擷取 driver 資訊

d.info
#輸出
{
    "currentPackageName": "com.android.systemui",
    "displayHeight": 2097,
    "displayRotation": 0,
    "displaySizeDpX": 360,
    "displaySizeDpY": 780,
    "displayWidth": 1080,
    "productName": "freedom_turbo_XL",
    "screenOn": true,
    "sdkInt": 29,
    "naturalOrientation": true
}           

8.2.2 擷取裝置資訊

會輸出測試裝置的所有資訊,包括電池,CPU,記憶體等

d.device_info
#輸出
{
    "udid": "61c90e6a-ba:1b:ba:46:91:0e-freedom_turbo_XL",
    "version": "10",
    "serial": "61c90e6a",
    "brand": "Schok",
    "model": "freedom turbo XL",
    "hwaddr": "ba:1b:ba:46:91:0e",
    "port": 7912,
    "sdk": 29,
    "agentVersion": "0.9.4",
    "display": {
        "width": 1080,
        "height": 2340
    },
    "battery": {
        "acPowered": false,
        "usbPowered": true,
        "wirelessPowered": false,
        "status": 2,
        "health": 2,
        "present": true,
        "level": 98,
        "scale": 100,
        "voltage": 4400,
        "temperature": 292,
        "technology": "Li-ion"
    },
    "memory": {
        "total": 5795832,
        "around": "6 GB"
    },
    "cpu": {
        "cores": 8,
        "hardware": "Qualcomm Technologies, Inc SDM665"
    },
    "arch": "",
    "owner": null,
    "presenceChangedAt": "0001-01-01T00:00:00Z",
    "usingBeganAt": "0001-01-01T00:00:00Z",
    "product": null,
    "provider": null
}           

8.2.3 擷取螢幕分辨率

# 傳回(寬,高)元組
d.window_size()
# 例 分辨率為 1080*1920
# 手機豎屏狀态傳回 (1080,1920)
# 橫屏狀态傳回 (1920,1080)           

8.2.4 擷取 IP 位址

# 傳回 ip 位址字元串,如果沒有則傳回 None
d.wlan_ip           

8.3 driver 全局設定

8.3.1 使用 settings 設定

檢視 settings 預設設定

d.settings
#輸出

{
    #點選後的延遲,(0,3)表示元素點選前等待 0 秒,點選後等待 3S 再執行後續操作
    'operation_delay': (0, 3),
    # opretion_delay 生效的方法,預設為 click 和 swipe
    # 可以增加 press,send_keys,long_click 等方式
    'operation_delay_methods': ['click', 'swipe'],
    # 預設等待時間,相當于 appium 的隐式等待
    'wait_timeout': 20.0,
    # xpath 日志
    'xpath_debug': False
}           

修改預設設定,隻需要修改 settings 字典即可

#修改延遲為操作前延遲 2S 操作後延遲 4.5S
d.settings['operation_delay'] = (2,4.5)
#修改延遲生效方法
d.settings['operation_delay_methods'] = {'click','press','send_keys'}
# 修改預設等待
d.settings['wait_timeout'] = 10           

8.3.2 使用方法或者屬性設定

  • http 預設請求逾時時間
# 預設值 60s, 
d.HTTP_TIMEOUT = 60            
  • 當裝置掉線時,等待裝置線上時長
# 僅當 TMQ=true 時有效,支援通過環境變量 WAIT_FOR_DEVICE_TIMEOUT 設定
d.WAIT_FOR_DEVICE_TIMEOUT = 70            
  • 元素查找預設等待時間
# 打不到元素時,等待 10 後再報異常
d.implicitly_wait(10.0)           
  • 打開 HTTP debug 資訊
d.debug = True
d.info
#輸出
15:52:04.736 $ curl -X POST -d '{"jsonrpc": "2.0", "id": "0eed6e063989e5844feba578399e6ff8", "method": "deviceInfo", "params": {}}' 'http://localhost:51046/jsonrpc/0'
15:52:04.816 Response (79 ms) >>>
{"jsonrpc":"2.0","id":"0eed6e063989e5844feba578399e6ff8","result":{"currentPackageName":"com.android.systemui","displayHeight":2097,"displayRotation":0,"displaySizeDpX":360,"displaySizeDpY":780,"displayWidth":1080,"productName":"freedom_turbo_XL","screenOn":true,"sdkInt":29,"naturalOrientation":true}}
<<< END           
  • 休眠
# 相當于 time.sleep(10)
d.sleep(10)           

8.4 亮滅屏

# 亮屏
d.screen_on()
# 滅屏
d.screen_off()           

8.5 螢幕方向

# 設定螢幕方向
d.set_orientation(value)
# 擷取目前螢幕方向
d.orientation           

value 值參考,隻要是元組中的任一一個值就可以。

# 正常豎屏
(0, "natural", "n", 0), 
# 往左橫屏,相當于手機螢幕順時針旋轉 90 度
# 現實中如果要達到此效果,需要将手機逆時針旋轉 90 度
 (1, "left", "l", 90),
# 倒置,這個需要看手機系統是否支援 , 倒過來顯示 
 (2, "upsidedown", "u", 180), 
# 往右橫屏,調整與往左相反,螢幕順時針旋轉 270 度
 (3, "right", "r", 270))           

8.6 打開通知欄與快速設定

  • 打開通知欄
d.open_notification()           
  • 打開快速設定
d.open_quick_settings()           

8.7 檔案導入導出

8.7.1 導入檔案

# 如果是目錄,這裡 "/sdcrad/" 最後一個斜杠一定要加,否則會報錯
d.push("test.txt","/sdcrad/")
d.push("test.txt","/sdcrad/test.txt")           

8.7.2 導出檔案

d.pull('/sdcard/test.txt','text.txt')           

8.8 執行 shell 指令

使用 shell 方法執行

8.8.1 執行非阻塞指令

output 傳回的是一個整體的字元串,如果需要抽取值,需要對 output 進行解析提取處理

# 傳回輸出和退出碼,正常為 0,異常為 1
output,exit_code = d.shell(["ls","-l"],timeout=60)           

8.8.2 執行阻塞指令(持續執行的指令)

# 傳回一個指令的資料流 output 為 requests.models.Response
output = d.shell('logcat',stream=True)
try:
    # 按行讀取,iter_lines 為疊代響應資料,一次一行
    for line in output.iter_lines():
        print(line.decode('utf8'))
finally:
    output.close()
源碼描述

    def shell(self, cmdargs: Union[str, List[str]], stream=False, timeout=60):
        """
        Run adb shell command with arguments and return its output. Require atx-agent >=0.3.3

        Args:
            cmdargs: str or list, example: "ls -l" or ["ls", "-l"]
            timeout: seconds of command run, works on when stream is False
            stream: bool used for long running process.

        Returns:
            (output, exit_code) when stream is False
            requests.Response when stream is True, you have to close it after using

        Raises:
            RuntimeError

        For atx-agent is not support return exit code now.
        When command got something wrong, exit_code is always 1, otherwise exit_code is always 0
        """           

8.9 session(目前已經被棄用)

8.10 停止 UI2 服務

因為有 atx-agent 的存在,Uiautomator 會被一直守護着,如果退出了就會被重新啟動起來。但是 Uiautomator 又是霸道的,一旦它在運作,手機上的輔助功能、電腦上的 uiautomatorviewer 就都不能用了,除非關掉該架構本身的 uiautomator

使用代碼停止

d.service("uiautomator").stop()           

手動停止

直接打開 ATX APP(init 成功後,就會安裝上),點選關閉 UIAutomator

以上,歡迎大家一起交流探讨。

免費領取:接口測試+性能測試+自動化測試+測試開發+測試用例+履歷模闆+測試文檔