PS:前言
工作中使用airtest遇到的一些問題(或者說坑的地方),記錄一下,順便把官網記錄的一些問題及解決辦法也拿過來梳理一下,做一下總結!后續也會持續補充,看到此篇文章的小伙伴也可以評論留言提供遇到的問題。
實際工作中的一些使用經驗<py腳本>
先了解一下高階py格式腳本
首先,新建一個純py腳本

設置腳本路徑,其它配置一般默認
新建完成后,會自動插入腳本初始化接口代碼,包含 cli_setup()、auto_setup() 和 simple_report() 方法
cli_setup:
當使用 python xx.py 來運行本文件,且不帶任何命令行參數時,則 if not cli_setup() 的判斷成立,自動使用 auto_setup 這個接口來對運行環境進行初始化。這樣只需要在寫 .py 腳本時,在 auto_setup() 里填寫好指定的參數就能直接用 python xx.py 指令來運行腳本了。
同時,傳統的 airtest run xx.air –-devices Android:/// 命令行運行方式也不受影響,只要腳本檢測到傳入了命令行參數(即代碼中的 if not cli_setup() 判斷不成立,不走到里面的 auto_setup() 初始化去),就依然優先使用命令行參數來初始化腳本運行環境的相關配置項。
auto_setup :
主要作用是連接設備、設置日志路徑。(不填入設備參數的情況下,都是嘗試連接第一台安卓設備)
simple_report:
生成報告的簡單接口,只需要在腳本中調用這個接口,然后傳入幾個必要的報告參數,就能夠在腳本運行完畢之后,按照要求自動生成Airtest報告
注意:純py腳本,需要自己設置生成報告,執行結束后才會產生報告,傳參要注意和auto_setup對應
auto_setup 方法解析
def auto_setup(basedir=None, devices=None, logdir=None, project_root=None, compress=None):
"""
自動設置運行環境,如果未連接設備,請嘗試連接android設備。
:param basedir: 腳本的Basedir, __file__也是可以接受的
:param devices: 連接列表中的設備uri
:param logdir: 默認為None表示沒有日志,設置為' ' True ' '
:param project_root: project root dir for `using` api. 獲取環境變量
:param compress: 截圖圖像的壓縮率,范圍為[1,99]的整數,默認為10
:Example:
>>> auto_setup(__file__)
>>> auto_setup(__file__, devices=["Android://127.0.0.1:5037/SJE5T17B17"],
... logdir=True, project_root=r"D:\\test\\logs", compress=90)
"""
if basedir: # __file__ 當前腳本的絕對路徑--例如:E:/PythonPRT/AirTest/a1/test.py
if os.path.isfile(basedir): # os.path.isfile(path) 如果path是一個存在的文件,返回True。否則返回False
basedir = os.path.dirname(basedir) # basedir是當前腳本__file__目錄的上級目錄
if basedir not in G.BASEDIR: # G.BASEDIR 默認是一個空列表[]
G.BASEDIR.append(basedir) # 如果當前腳本路徑不在G.BASEDIR列表內就添加進去
if devices: # devices為傳入的設備參數 devices=["android://127.0.0.1:5037/8B9X16KBS"])
for dev in devices: # 找到傳入的設備
connect_device(dev) # 連接設備
if logdir: # logdir 為傳入的參數:log文件夾的路徑,用於設置日志文件和截圖的日志目錄
logdir = script_log_dir(basedir, logdir) # 查看basedir上級目錄有沒有logdir文件夾
set_logdir(logdir) # 如果沒有該目錄,就新建一個目錄
if project_root:
ST.PROJECT_ROOT = project_root # ST.PROJECT_ROOT 大概意思是獲取環境變量,默認為None,一般不用管
if compress:
ST.SNAPSHOT_QUALITY = compress # 截圖圖像的壓縮率,范圍為[1,99]的整數,默認為10
在我使用過程中,由於結合了unittest框架,就拆分了原有的auto_setup方法,一個用來單獨建立設備連接、一個用來單獨設置日志文件夾
simple_report 方法解析
simple_report 接口,其實是1個簡化版的生成報告的接口
filepath:腳本文件的路徑,可以直接傳入變量
__file__logpath :log內容所在路徑,如為 True ,則默認去當前腳本所在路徑找log內容
logfile :log.txt的文件路徑
output :報告的到處路徑,必須以 .html 結尾
下面為源碼:
def simple_report(filepath, logpath=True, logfile=None, output=HTML_FILE):
path, name = script_dir_name(filepath)
if logpath is True:
logpath = os.path.join(path, getattr(ST, "LOG_DIR", DEFAULT_LOG_DIR))
rpt = LogToHtml(path, logpath, logfile=logfile or getattr(ST, "LOG_FILE", DEFAULT_LOG_FILE), script_name=name)
rpt.report(HTML_TPL, output_file=output)
如果不指定任何參數,該接口會使用默認的參數生成1份HTML格式的報告,output='log.html' 表示在當前腳本路徑下生成名為 log.html 的airtest報告:
from airtest.report.report import simple_report
simple_report(__file__)
如果指定了 output 參數,則會按指定路徑生成報告:
from airtest.report.report import simple_report
auto_setup(__file__, logdir=True)
# 此處省略N條用例腳本
simple_report(__file__,logpath=True,output=r"D:\test\report02\log.html")
另外需要注意,要在用例腳本之后調用這個生成報告的接口,如在腳本開頭調用,則意味着還沒有運行后面的用例步驟,就已經生成了一份報告,最終不論腳本運行情況如何,都只能拿到一份空的測試報告。
注意點:
1.場景:很多同學找到了報告所在的本地文件夾之后,就直接把這個文件夾打包發給同事/領導查看,結果發現,同事/領導根本不能正常查看這個報告
2.分析:出現這個問題的原因其實是,使用1simple_report1生成的html報告,里面的一些圖片路徑和靜態資源路徑是 絕對路徑 。當報告被發到別的電腦上時,html再想通過絕對路徑找到這些圖片資源和靜態資源,就不可能了,所以才造成 其他人看到的報告完全沒有圖片和相應樣式 的情況
3.解決方案:
①若是在AirTest IDE中運行時,IDE中提供了導出報告的功能,可以通過右鍵點擊腳本名稱,選擇
導出報告,之后再選擇導出報告的存放路徑即可,導出報告之后,圖片資源和靜態資源的路徑都被改成了 相對路徑 ,並且文件夾中還 打包了對應的靜態資源文件 。這樣我們再把導出好的報告發到別的電腦查看時,就不會出現上述問題了②但如果需要通過腳本的形式來導出1份報告,則需要用到logtohtml()方法,參考下面logtohtml方法解析
4.總結:如果不需要通過腳本的形式導出報告,僅需要生成報告在本地查看的話,只要使用
simple_report接口即可
logtohtml方法解析
這個接口是用於導出報告,參數相對於 simple_report() 就復雜的多了,包含:
script_root,腳本路徑
log_root,log文件的路徑
static_root,部署靜態資源的服務器路徑
export_dir,導出報告的存放路徑
script_name,腳本名稱
logfile,log文件log.txt的路徑
lang,報告的語言(中文:zh;英文:en)
plugins,插件,使用了poco或者airtest-selenium會用到
示例如下,我們在指定路徑 D:\test\report02 中導出了 D:\test\report01.air 腳本的運行報告,報告語言為英文:
from airtest.report.report import LogToHtml
h1 = LogToHtml(script_root=r'D:\test\report01.air', log_root=r"D:\test\report01.air\log", export_dir=r"D:\test\report02" ,logfile=r'D:\test\report01.air\log\log.txt', lang='en', plugins=None)
h1.report()
ps:若不使用static_root參數時,則每次導出報告都會 打包靜態資源文件 。雖然這樣我們把導出好的報告發到別的電腦查看時,就不會出現確實靜態資源無法查看的問題,但每次執行都要打包靜態資源,每次發給別人也要把靜態資源打包一起發送,不僅使報告文件變大,也會非常繁瑣
static_root
每次導出報告時,目錄下都會有一份靜態資源文件static,它包含了報告中的css和js等文件。一般來說,除非報告的樣式做了某些更新,否則這些靜態資源文件都是固定不變的。
所以我們可以將這些資源文件部署到靜態資源文件服務器上,用例如 https://host:port/static/css/ 的路徑來訪問它。然后在生成報告時,將這個部署出來的服務器地址作為 tatic_root的參數傳過去:
h1 = LogToHtml(__file__, log_root=LOG_DIR, static_root='http://xx.xx',
export_dir=BASE_DIR, logfile=LOG_DIR + '\\log.txt', lang='zh',plugins=["poco.utils.airtest.report"])
h1.report()
這樣報告中會默認去訪問static_root參數傳入的服務器地址,在上面讀取靜態資源文件
這樣可以很好地避免導出報告時重復拷貝這些資源文件造成的磁盤空間占用。
可以使用七牛雲、又拍雲等部署靜態資源文件,我用的是又拍雲
log方法解析
log() 接口方便插入用戶自定義的一些log信息,將會被顯示在Airtest報告中。log接口支持傳入4個參數:
args ,可以是字符串、非字符串或者 traceback 對象;
timestamp ,用於自定義當前log的時間戳;
desc ,用於自定義log的標題;
snapshot ,表示是否需要截取一張當前的屏幕圖像並顯示到報告中
說明,在air腳本中不需要傳log()方法以及生成報告方法,air腳本執行會自動配置生成
用法演示:
auto_setup(__file__, devices=["android://127.0.0.1:5037/8B9X16KBS?cap_method=JAVACAP&&ori_method=ADBORI&&touch_method=MAXTOUCH"])
#用例方法內容這里忽略
def test()
...
...
# 執行用例
test()
simple_report(__file__,logpath=True)
上面這種是正常用法,弊端是一旦前面有用例腳本執行失敗,終止了整個腳本的運行,即還沒有執行到生成報告的語句時,腳本運行就已經停止了,這樣也不能夠正常生成報告。
auto_setup(__file__, devices=["android://127.0.0.1:5037/8B9X16KBS?cap_method=JAVACAP&&ori_method=ADBORI&&touch_method=MAXTOUCH"])
#用例方法內容這里忽略
def test()
...
...
try:
# 執行用例
test()
except Exception as e:
log(e,timestamp=time.time(),desc='報錯信息',snapshot=True)
finally:
# finally:保證最后都能生成報告,執行出錯也會生成報告,並生成錯誤log
simple_report(__file__,logpath=True)
上面設置,當腳本運行報錯時except拋出異常,不論腳本是否運行失敗,最終都會生成1份運行報告
也可以用於插入自定義的一些log信息,並顯示在Airtest報告中
修改simple_report接口(自定義airtest報告靜態資源)
目的:前面說結合了unittest框架,拆分了原有的auto_setup方法,一個用來單獨建立設備連接、一個用來單獨設置日志文件夾。使用simple_report則可以每步用例都能生成一份單獨的airtest測試報告
但由於源碼中該接口的默認設置,報告靜態資源讀取的為python/lib/sit-pages/airtest/report目錄下的靜態資源,所以使用中有一些局限,於是研究了源碼中simple_report方法,做了一些修改
先貼下simple_report源碼:
def simple_report(filepath, logpath=True, logfile=None, output=HTML_FILE):
path, name = script_dir_name(filepath)
if logpath is True:
logpath = os.path.join(path, getattr(ST, "LOG_DIR", DEFAULT_LOG_DIR))
rpt = LogToHtml(path, logpath, logfile=logfile or getattr(ST, "LOG_FILE", DEFAULT_LOG_FILE), script_name=name)
rpt.report(HTML_TPL, output_file=output)
通過源碼發現也調用了LogToHtml方法,我們再進入LogToHtml方法內:

之前講過static_root參數的作用,現在就來分析這個參數
static_root默認為空,x or y if x is False,then y,else x,所以這里取值為STATIC_DIR
查看STATIC_DIR的值
首先__file__是report.py文件的path:
C:/Program Files/Python37/Lib/site-packages/airtest/report/report.py
語法:os.path.dirname(path)
功能:去掉文件名,返回目錄
所以,STATIC_DIR的值為:
C:/Program Files/Python37/Lib/site-packages/airtest/report
到這里就發現了,原來simple_report方法使用的靜態資源是python安裝目錄下的
所以我們要修改這個參數,使其指向我們項目目錄內相對路徑下的靜態資源位置
怎么修改,其實很簡單,就是修改STATIC_DIR參數,把這個參數替換為我們自己重置好之后的靜態資源目錄,可以是項目報告目錄內,也可以是服務器靜態資源托管地址,
然后我們再在simple_report內把調用的LogToHtml方法修改為我們自己改過的新的lotohtml方法,這樣就解決了報告靜態資源讀取的問題
AirTest常見的問題<從官網摘錄>
設備連接參數怎么理解?
IDE提供了3個備用的連接參數: Use javacap 、Use ADB orientation 和 Use ADB touch ;
① 第一個 Use javacap ,是給部分無法正常看到手機畫面、minicap初始化失敗 的手機或設備用的,所以模擬器看到黑屏、部分特殊的平板等設備可以考慮勾選這個選項
② 第二個 Use ADB orientation 是 屏幕旋轉 的,如果在安卓手機屏幕旋轉方向檢測有問題、或者是部分特殊的平板無法顯示正確的屏幕方向時可以勾選
③ 第三個 Use ADB touch 是 發送adb指令來點擊屏幕 ,效果很差,速度也很慢,不建議勾選,只有在部分無法點擊屏幕的特殊安卓設備上才需要使用(例如智能后視鏡、特殊型號的平板等設備上)
正常情況下,手機都可以點擊,如果無法被點擊(比如小米設備),一般都是因為手機設置有選項漏了打開,特別是 小米設備要注意開啟允許模擬點擊 的設置
如何取消腳本執行過程刷新的大量log信息
如果你不想這些log信息干擾你提取有效的報錯信息時,你可以在腳本代碼開頭加上log級別的設定:
# -*- encoding=utf8 -*-
import logging
logger = logging.getLogger("airtest")
logger.setLevel(logging.ERROR)
這樣運行時只會在初始化手機時會有少量log輸出,初始化完畢后就能夠對logger進行過濾了
在IDE中如何引入第三方庫?
AirtestIDE內置了一個精簡的python環境,缺少很多第三方庫;如果需要在IDE中引入各種第三方庫,可以先在本地的python環境中裝好,再設置IDE使用本地的python環境,具體步驟如下:
① 請自行在你的電腦上安裝屬於你自己的Python(目前已經支持3.9),然后再安裝對應的依賴庫。你可以通過一些環境管理方案來管理你的python環境,例如virtualenv
② 在剛才安裝好的本地Python環境中,安裝airtest相關的依賴庫,詳細內容請參考文檔:https://airtest.doc.io.netease.com/IDEdocs/run_script/1_useCommand_runScript/#python
③ 在本地安裝完屬於你自己的python環境后,再在IDE里面打開選項——設置——手動選擇自定義python.exe路徑即可,詳細內容請參考文檔:https://airtest.doc.io.netease.com/IDEdocs/settings/1_ide_settings/#python
RuntimeError : minitouch setup timeout
出現這個報錯,最常見的是以下倆種情況:
① 手機系統是MIUI11,此時我們需要在點擊“connect”按鈕之前,把 “use Javacap + use orientation” 這兩個選項勾選上,再點擊“connect”按鈕即可正常使用
② 手機的安卓版本是Android10,此時僅需要把IDE更新到最新版本即可;如果IDE使用的是本地的python環境,那還需要把本地python環境的Airtest更新到最新版本。
ADB版本沖突
在Windows環境中運行Airtest腳本時,假如運行環境本地已經存在有 adb.exe (例如裝了android_sdk, 或者是別的工具帶有adb.exe),並且ADB版本與Airtest中使用的不同,運行腳本時就可能會出現各種報錯。
常見情況下,我們會看到log中包含這樣的語句:
adb server version (40) doesn't match this client (39); killing...
* daemon started successfully *
如log所示,我們可以看到環境里面使用了40和39版本,版本沖突導致報錯。解決辦法是將本地所有的adb.exe統一成同樣的版本就行。
另外,adb版本沖突,還會容易導致設備斷開,出現報錯。
解決辦法依舊是將本地所有的adb.exe統一成同樣的版本。
具體操作方法:
首先查看系統adb版本:進入cmd——adb version
再進入本地airtest\core\android\static\adb\windows路徑查看airtest的adb版本
如果不一致,就將airtest中的adb復制替換本地的adb即可
poco無限重啟的解決辦法
① 如果開了網絡代理的話,需要先 關閉各種代理和VPN ,否則可能會影響到poco通訊
② 檢查手機助手內是否對 pocoservice.apk 做了限制,例如在某版本的華為手機中需要開啟 允許自啟動 和 允許后台活動
③ 不能和uiautomator同時啟動,否則會相互沖突
④ 可以嘗試 重啟手機 看看是否會恢復
用Airtest測試iOS一定要用macOS嗎?
① 使用 xcode 部署 iOS-Tagent 需要在macOS完成;
② 部署好以后可以在macOS或Windows機器上連接到iOS手機進行測試。
如何刪除iOS輸入框的內容
① 對於Android平台,我們可以使用多種方法來刪除輸入框的內容,比如使用 keyevent 接口: keyevent("KEYCODE_DEL") ;或者使用Poco的 set_text() 方法: poco("xxx").set_text("") ;
②但對於iOS平台來說,暫不支持 set_text() 接口,也不支持 keyevent("KEYCODE_DEL") ,所以這倆種方法對於iOS的輸入框來說是無效的。iOS支持 text() 方法,所以我們可以用 text("\b",False) ,來實現iOS輸入框內容的刪除
本地pip list可以找到airtest,但pycharm里找不到
這種情況很有可能就是pycharm使用了 虛擬環境的解釋器 。
可以隨意運行1個項目,然后查看運行結果窗口顯示的解釋器是不是你在本地安裝的解釋器的路徑,如果不是,一般會帶有 venv 的字樣,例如:
D:\test\vene\Scripts\python.exe D:\test\test.py
1
這種情況只需要將pycharm從虛擬環境切換到安裝了Airtest的本地環境即可
連接模擬器出現黑屏
① 先嘗試在連接模擬器之前下拉勾選 “Use javacap” 選項,之后再點擊“connect”按鈕連接模擬器
② 如果仍然黑屏,斷開模擬器,下拉勾選 “Use javacap” 和 “Use ADB orientation” 這倆個選項,再次連接即可
③ 如上述方式都不見效,可以上GitHub提一個issue,貼上使用的IDE和模擬器版本詳情(GitHub地址:https://github.com/AirtestProject/AirtestIDE/issues)
模擬器勾選上連接參數之后還是連不上
連接模擬器的時候,我們需要勾選上一些備選的連接參數才能連接上模擬器,或者不讓模擬器黑屏。
比如連接雷電時需要勾選上 Use javacap,而連接夜神的時候需要勾選上 Use javacap 和 Use ADB orientation 這倆個選項。
但是在一些版本的模擬器中,即使勾選上了連接參數,仍然可能連接不上模擬器
這是因為在連接這些版本的模擬器時,不能自動安裝上 Yosemite.apk ,我們可以通過手動安裝這個 apk ,再重新連接模擬器即可。
報告可以導出發給別人看嗎?
Airtest的報告是可以打包發給別人看的。
① 想要導出報告發給別人觀看,我們需要生成報告的命令中傳入 --export 參數,這樣就可以將 包含靜態資源文件和圖片文件的報告 導出到一個指定的文件夾內,之后直接將整個文件夾發送給別人觀看即可。
② 如果生成報告時不傳入 --export 參數,那么報告中的靜態資源文件和圖片文件將使用 絕對路徑 來訪問,此時將整個文件夾發給別人觀看,別人也是無法正常觀看的。
復制IDE中log窗口生成的報告在命令行執行報錯
常見的情況可能有如下幾種:
① 復制的命令路徑中含有空格,導致程序報錯:找不到文件或者路徑;只需要將命令中的路徑用英文模式下的雙引號括起來即可。
② 命令行中包含了&這樣的符號,可能會導致命令行被中斷,常見於一些勾選了 “use javacap” 這樣的選項后連接的設備。但是&這個字符需要轉義才能夠生效:Windows下改寫成^&,MAC下改寫成/&
ImportError: DLL load failed: 找不到指定模塊
常見的DLL報錯會出現在以下2種情況中:
① 在 cv2 模塊報 ImportError: DLL load failed: 找不到指定模塊 的錯
根本原因應該是DLL文件的缺失,你可以直接下載一個最新版本的AirtestIDE,在解壓后的目錄中找到 api-ms-win-downlevel-shlwapi-l1-1-0.dll 和 IEShims.dll 兩個DLL文件(或者自行在網上搜索這倆個DLL文件也是可以的),然后將它們復制到 C:\Windows\System32 目錄,重新運行代碼即可解決。
② 若在 win.py 中 import win32api 時報 DLL load failed :
<Module>
import win32api
ImportError: DLL load failed: 找不到指定的程序。
建議您運行下列指令,更新為223版本的 pywin32:
pip uninstall pywin32
pip install pywin32==223
各種常用的pip命令
① 安裝Airtest庫: pip install airtest
② 安裝poco庫: pip install pocoui
③ 更新Airtest: pip install -U airtest
④ 更新Poco: pip install -U pocoui
⑤ 卸載Airtest庫: pip uninstall airtest
特別注意:Poco依賴庫是 pocoui 而不是 poco,如果你發現你的環境里面同時存在 poco 和 pocoui ,請務必把 poco 卸載了,留下 pocoui即可。
另外,如果你的電腦同時安裝了 python3 和 python2 ,在不同python環境里面使用pip命令時可以使用如下方法:
# Python2
pip2 install XXX
python2 -m pip install XXX
# Python3
pip3 install XXX
python3 -m pip install XXX
AirTest框架源碼目錄結構解析
摘自:微信公眾號(測試工程師小站)

