心路歷程 | 備注 | 日期 |
---|---|---|
Airtest蚂斤,pycharm安裝见擦,天賦用例得簡(jiǎn)單編寫 | 2019年11月09日之前 | |
項(xiàng)目進(jìn)行得時(shí)候發(fā)現(xiàn)有一些問(wèn)題簿透,所以在簡(jiǎn)書上開(kāi)始記錄問(wèn)題 | 創(chuàng)建沼头,問(wèn)題記錄 | 2019年11月09日 |
之前把2個(gè)文檔分開(kāi)成airtest自動(dòng)化測(cè)試和airtest遇到得問(wèn)題 | 文檔整合 | 2020年3月11日 |
Airtest轉(zhuǎn)移到pycharm爷绘,讓項(xiàng)目更加得規(guī)范化 | 規(guī)范項(xiàng)目以及測(cè)試用例 | 2020年3月11日 |
使用unittest測(cè)試框架編寫用例,了解pytest框架 | 2020年5月7日 | |
用例順序并發(fā) | 多個(gè)用例按順序一個(gè)個(gè)分配到空閑手機(jī)上進(jìn)行并發(fā)进倍,簡(jiǎn)版并發(fā)土至,有疑問(wèn)加群:5406855 | 2020年5月7日 |
修改pytest框架 | 從序號(hào)20開(kāi)始記錄遇到相關(guān)的問(wèn)題 | 2020年8月27日 |
由于項(xiàng)目問(wèn)題,導(dǎo)致無(wú)法繼續(xù)更新,后續(xù)會(huì)在看看從別的項(xiàng)目繼續(xù)進(jìn)行學(xué)習(xí) | 目前暫時(shí)不更新airtest相關(guān)內(nèi)容,最近會(huì)更新一些安全測(cè)試以及一些小工具 | 2021年10月14日 |
項(xiàng)目:基于unity3d項(xiàng)目
涉及相關(guān)模塊:關(guān)卡,戰(zhàn)斗猾昆,商城陶因,裝備,強(qiáng)化垂蜗,天賦等
記錄:Airtest+poco+pycharm自動(dòng)化游戲測(cè)試過(guò)程中發(fā)現(xiàn)的問(wèn)題以及解決的方法
目標(biāo):自動(dòng)化打包安裝楷扬,多機(jī)器并發(fā)測(cè)試生成報(bào)告
其他:相關(guān)敏感的字眼會(huì)修改為XXX解幽。目前能力有限,有很多大佬的框架都是非常厲害烘苹,鑒于自己的能力不足躲株,只能一邊學(xué)習(xí)一邊摸索游戲測(cè)試相關(guān)的同學(xué)也可以加群交流:5406855
測(cè)試用例1:
# -*- encoding=utf8 -*-
__author__ = "Administrator"
#天賦測(cè)試需要跳過(guò)新手
import unittest
from airtest.core.api import *
from airtest.core.android.adb import *
sys.path.append(r"E:\test\xxxx")
using("test_function")
from lasthero_report import LastHeroReport
import lasthero_gm as gm
from poco.drivers.unity3d import UnityPoco
class Talent(unittest.TestCase):
@classmethod
def setUpClass(cls):
global poco, lhr
poco = UnityPoco()
lhr = LastHeroReport(__file__, os.path.basename(__file__))
def setUp(self):
pass
# 天賦9級(jí)沒(méi)有開(kāi)啟
def test_1_level(self):
'''等級(jí)9級(jí)不能打開(kāi)天賦'''
a = TalentSet(9)
a.setlevel()
# 天賦10級(jí)開(kāi)啟
def test_2_level(self):
'''等級(jí)10級(jí)正常打開(kāi)天賦'''
a = TalentSet(10)
a.setlevel()
# 天賦11級(jí)正常開(kāi)啟
def test_3_level(self):
'''等級(jí)11級(jí)正常打開(kāi)天賦'''
a = TalentSet(11)
a.setlevel()
print("天賦11")
def tearDown(self):
poco("xxx").click()
@classmethod
def tearDownClass(cls):
lhr.creat_report()
class TalentSet:
def __init__(self, x):
self.x = x
poco = UnityPoco()
self.gm = gm.GM(poco)
def setlevel(self):
self.gm.gm_open()
self.gm.xxx(str(self.x))
self.gm.gm_close()
sleep(1)
poco("xxx").click()
if self.x == 9:
assert_equal(poco("xxx").exists(), False, "天賦9級(jí)沒(méi)開(kāi)啟")
elif self.x == 10:
assert_equal(poco("xxx").exists(), True, "天賦10級(jí)打開(kāi)")
elif self.x == 11:
assert_equal(poco("xxx").exists(), True, "天賦11級(jí)打開(kāi)")
測(cè)試用例2:
import unittest
from poco.drivers.unity3d import UnityPoco
from airtest.core.api import *
from airtest.core.android.adb import *
sys.path.append(r"E:\test\xxx")
using("test_function")
from lasthero_report import LastHeroReport
class Shop(unittest.TestCase):
# 類屬性
video_num = 5
@classmethod
def setUpClass(cls) -> None:
global poco,lhr
poco = UnityPoco()
lhr = LastHeroReport(__file__, os.path.basename(__file__))
def setUp(self) -> None:
pass
def test_1_video_diamond_num(self):
"""
看視頻兌換鉆石次數(shù)檢查
"""
if poco("xxx").exists():
pass
else:
poco("xxx").click()
Shop.video_num = int(poco("xxx").get_text())
assert_equal(Shop.video_num,4,"今日看視頻兌換次數(shù)為5")
print(Shop.video_num)
def test_2_accelerate(self):
"""
檢查打開(kāi)加速提示文字是否正常
"""
try:
poco("xxx").swipe([0, -4], duration=1)
sleep(3)
poco("xxx").click()
sleep(1)
assert_equal(poco("xxx").exists(),True,"正常顯示加速提示文字")
sleep(1)
poco("xxx").click()
except Exception as e:
log(e)
def tearDown(self) -> None:
pass
@classmethod
def tearDownClass(cls) -> None:
lhr.creat_report()
if __name__ == "__main__":
unittest.main()
生成報(bào)告封裝類:
class LastHeroReport:
def __init__(self,filepath,filename):
self.file_name_list = filename.split(".")
self.log_dir = os.path.dirname(os.getcwd()) + "/log/" + self.file_name_list[0]
self.atr = os.path.dirname(os.getcwd()) + "/airtest_report/"
if not os.path.exists(self.log_dir):
os.makedirs(self.log_dir)
auto_setup(logdir=self.log_dir)
self.filepath = filepath
def creat_report(self):
simple_report(self.filepath, logpath=self.log_dir, logfile='log.txt', output=self.atr + self.file_name_list[0] +'_log.html')
main:
# 用例順序并發(fā)
from multiprocessing import Pool,Manager
import unittest
from airtest.core.android.adb import *
from airtest.core.api import *
sys.path.append(r"E:\test\xxxx")
cash_path = "E:/test/xxxx"
def g1_run(tag, q):
t = q.get()
connect_device("Android://127.0.0.1:5037/"+t)
a = []
suit = addsuit()
for s in suit:
a.append(s)
runner = unittest.TextTestRunner()
runner.run(a[tag])
q.put(t)
def addsuit():
discover = unittest.defaultTestLoader.discover(cash_path,
pattern="test*.py",
top_level_dir=None)
return discover
def getdevices():
devices = []
adb = ADB()
devices_liset = adb.devices()
for i in devices_liset:
devices.append(i[0])
print(devices)
return devices
if __name__ == "__main__":
q = Manager().Queue()
a = []
dev = []
dev = getdevices()
pool = Pool(len(dev))
for i in dev:
q.put(i)
suit = addsuit()
for i in suit:
a.append(i)
i = len(a)
for tag in range(i):
pool.apply_async(g1_run, (tag, q,))
pool.close()
pool.join()
問(wèn)題匯總:
1.如何導(dǎo)入其他air文件方法
from airtest.core.api import using
using("actionset.air")
from actionset import sign_board,authorize,callten
2.在python中運(yùn)行airtest腳本
os.system('airtest run C:/Users/Administrator/AppData/Local/Temp/AirtestIDE/scripts/main.air --device Android://127.0.0.1:5037/HKL48N6K --log log/')
--device Android://127.0.0.1:5037/HKL48N6K 中的--device是標(biāo)識(shí),不能直接去掉镣衡,這個(gè)標(biāo)識(shí)是安卓手機(jī)
3.在python中運(yùn)行完腳本后霜定,輸出和airtest中一樣的報(bào)告
os.system('airtest report C:/Users/Administrator/AppData/Local/Temp/AirtestIDE/scripts/main.air --log_root D:/pythonwork/venv/log --outfile D:/pythonwork/venv/log/log.html --lang zh')
4.在airtest中直接調(diào)用adb
(1)獲取應(yīng)用列表
adb shell dumpsys package > package.txt
(2)在package.txt查找自己需要啟動(dòng)的包名,及main activity廊鸥,如
com.androlua.compass/com.androlua.Main
(3)在airtest中連接adb并且調(diào)用app
dev = connect_device("Android://127.0.0.1:5037/xxxxxx")
dev.shell("am start -n com.xxxx.xxxx/com.unity3d.xxxx.xxxx")
5.在airtest內(nèi)導(dǎo)入py文件
在airtest路徑下新建一個(gè)文件夾或者新建py文件望浩,直接from 文件夾.文件 import 方法即可
6.airtest在start_app后再進(jìn)行poco初始化
ConnectionResetError: [WinError 10054] 遠(yuǎn)程主機(jī)強(qiáng)迫關(guān)閉了一個(gè)現(xiàn)有的連接
所以必須啟動(dòng)后再進(jìn)行初始化,如下:
start_app('com.xxxx.test')
assert_exists(APP啟動(dòng)后的圖片, "是否啟動(dòng)APP")
poco = UnityPoco()
7.使用poco().click()出現(xiàn)了偏移時(shí)解決方法(只適用于控件框范圍內(nèi))
官方說(shuō)明:點(diǎn)擊默認(rèn)點(diǎn)在 anchorPoint 上黍图,每個(gè)UI都會(huì)有一個(gè) anchorPoint 曾雕,也就是檢視器(Inspector)中UI包圍盒的那個(gè)紅點(diǎn),大部分情況下 anchorPoint 都在UI包圍盒的正中央助被。如果想指定其他的點(diǎn)擊位置剖张,可以傳一個(gè)參數(shù)到 click 方法中,這個(gè)參數(shù)是一個(gè)用list或tuple表示的2維向量揩环,其 [x, y] 值分別表示相對(duì)于包圍盒左上角的偏移量搔弄,左上角為 [0, 0] ,右下角為 [1, 1]
8.poco判斷界面是否存在或者控件是否存在該界面
第一種情況
assert poco("控件name").exists(),"天賦10級(jí)打開(kāi)"
第二種情況
assert poco("控件name").get_text() == "金幣購(gòu)買","金幣不足跳轉(zhuǎn)兌換界面"
9.poco查找控件的時(shí)候丰滑,一開(kāi)始用的是poco("控件name")顾犹,此時(shí)如果界面上相同name的時(shí)候可以用poco(text="控件text內(nèi)容")
10.由于有時(shí)候會(huì)出現(xiàn)項(xiàng)目覆蓋,導(dǎo)致出現(xiàn)ConnectionResetError: [WinError 10054] 遠(yuǎn)程主機(jī)強(qiáng)迫關(guān)閉了一個(gè)現(xiàn)有的連接
覆蓋后需要重新在maincamera中添加pocomanaget
11.使用poco語(yǔ)句的斷言導(dǎo)致在報(bào)告中沒(méi)有顯是斷言褒墨,暫時(shí)使用airtest封裝的斷言炫刷,后續(xù)有方法在更新上來(lái)
1.assert poco("lg_talentInstructions").exists(),"天賦11級(jí)打開(kāi)"
2.assert_equal(poco("lg_talentInstructions").exists(), True, "天賦11級(jí)打開(kāi)")
12.轉(zhuǎn)移airtest上得用例,讓項(xiàng)目規(guī)范化
新建pycharm項(xiàng)目時(shí)郁妈,導(dǎo)入了用例后需要進(jìn)行pip安裝airtest以及pocoui包
如果當(dāng)前測(cè)試用例中有airtest的找圖的方法時(shí)浑玛,轉(zhuǎn)移到pycharm中要把圖片也移動(dòng)過(guò)去
test_case:存放測(cè)試用例
test_funcion:寫了一些公共得方法
test_image:用例中需要用到得一些圖片
13.遷移項(xiàng)目得時(shí)候進(jìn)行調(diào)試,發(fā)現(xiàn)了一些問(wèn)題噩咪,所以想在airtestide中進(jìn)行調(diào)試會(huì)出現(xiàn)如下圖的報(bào)錯(cuò)顾彰,但是看了一下卻是沒(méi)問(wèn)題的,直接在pycharm運(yùn)行也不會(huì)報(bào)錯(cuò)胃碾,后面請(qǐng)教了一些大佬涨享,airtestide中編寫的任務(wù)代碼會(huì)隱式的封裝成unittest類函數(shù),所以u(píng)nittest里嵌套了unittest導(dǎo)致報(bào)錯(cuò)
14.在調(diào)試的時(shí)候誤以為自己本機(jī)是有使用venv環(huán)境仆百,發(fā)現(xiàn)運(yùn)行腳本的時(shí)候不是使用venv路徑的下的adb,所以開(kāi)始看是什么原因?qū)е隆?/p>
<airtest.core.android.adb> C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\airtest\core\android\static\adb\windows\adb.exe devices
一開(kāi)始通過(guò)網(wǎng)上查找厕隧,可能是配置解析器沒(méi)有弄好,所以一開(kāi)始再弄解析器,當(dāng)時(shí)遇到幾個(gè)問(wèn)題:
1.由于自己本地的pycharm進(jìn)行了漢化吁讨,所以導(dǎo)致圖1圖2一開(kāi)始就顯示異常帖族,圖1漢化的時(shí)候直接顯示報(bào)錯(cuò)的一些文字信息,無(wú)法判斷是對(duì)新項(xiàng)目進(jìn)行的設(shè)置挡爵,第二個(gè)是再配置好解析器(實(shí)際配置的是新項(xiàng)目)后進(jìn)行運(yùn)行,此處發(fā)現(xiàn)一直運(yùn)行不了甚垦,繼續(xù)查資料茶鹃,說(shuō)是運(yùn)行配置沒(méi)有選擇python解析器,當(dāng)時(shí)找到配置解析器時(shí)發(fā)現(xiàn)左側(cè)文件一開(kāi)始選中艰亮,然后選擇其他文件后無(wú)法點(diǎn)擊回去闭翩,并且右側(cè)顯示空白,所以刪除了本地漢化后迄埃,重新打開(kāi)運(yùn)行配置的時(shí)候就正常顯示疗韵,并且發(fā)現(xiàn)一開(kāi)始設(shè)置的解析器也是針對(duì)新項(xiàng)目,所以此時(shí)也修改回來(lái)侄非,后續(xù)如果有同事電腦上的環(huán)境有不同版本的python再進(jìn)行venv的配置
15.使用pycharm調(diào)用到adb的時(shí)候出現(xiàn)如下報(bào)錯(cuò):
報(bào)錯(cuò):adb server version (40) doesn't match this client (41); killing...
需要替換的adb版本路徑:C:\Users\Administrator\AppData\Local\Programs\Python\Python37\lib\site-packages\airtest\core\android\static\adb\windows\
這個(gè)錯(cuò)是由于電腦上有2個(gè)adb版本導(dǎo)致的蕉汪,接著開(kāi)始替換電腦上的adb版本,從環(huán)境變量中查看了一下配置了adb的環(huán)境變量都替換掉逞怨,之后再運(yùn)行依舊還是有報(bào)錯(cuò)者疤,后面發(fā)現(xiàn)airtest調(diào)用當(dāng)前路徑的adb,所以只要找到airtest第三方庫(kù)的路徑下的adb替換即可叠赦,因?yàn)闆](méi)有使用venv環(huán)境驹马,所以包還是下載到本地的python路徑
16.目前先使用unittest框架,后續(xù)再了解下pytest框架除秀,先做出簡(jiǎn)單的用例并發(fā)
17.進(jìn)程池通信的小坑
#以下會(huì)導(dǎo)致多進(jìn)程不會(huì)執(zhí)行
q = Queue()
pool = Pool(2)
pool.apply_async(g1_run, (q,))
#進(jìn)程池內(nèi)通信需要使用Manager().Queue()
q = Manager().Queue()
pool = Pool(2)
pool.apply_async(g1_run, (q,))
18.版本發(fā)完后有空進(jìn)行一下調(diào)試糯累,增加每個(gè)腳本前啟動(dòng)APP和結(jié)束APP,接著在調(diào)試的過(guò)程發(fā)現(xiàn)并發(fā)突然不好使了册踩,啟動(dòng)APP是同事寫的泳姐,這里創(chuàng)建了一個(gè)Android對(duì)象,使用了check_app檢查手機(jī)上的app是否存在和啟動(dòng)app棍好。后面經(jīng)過(guò)排查仗岸,使用了airtest的start_app卻是可以正常并發(fā)運(yùn)行,查看了源碼發(fā)現(xiàn)在connect_device的時(shí)候調(diào)用過(guò)init_device借笙,獲取到了當(dāng)前的設(shè)備扒怖,而我們?cè)趧?chuàng)建command = Android()的時(shí)候并沒(méi)有指定設(shè)備,當(dāng)設(shè)備為空的時(shí)候就去找默認(rèn)的設(shè)備业稼,所以在并發(fā)的時(shí)候都是針對(duì)同一個(gè)手機(jī)進(jìn)行了檢查和啟動(dòng)盗痒,而沒(méi)有指定設(shè)備,所以添加個(gè)設(shè)備參數(shù)即可
錯(cuò)誤的源碼:
def startapps():
command = Android()
if not command.check_app(packgename):
print("當(dāng)前未安裝App!!!正在安裝所需的App")
install(apkpath)
command.start_app(packgename)
sleep(5)
if pocoo(text="xx申請(qǐng)以下權(quán)限").wait_for_appearance(30):
pocoo(text="允許").click()
修改:
def startapps(dev):
command = Android(dev)
19.運(yùn)行腳本比較久的時(shí)候,突然提示---[pocoservice.apk] stdout: b'INSTRUMENTATION_RESULT: shortMsg=Process crashed.\r\nINSTRUMENTATION_CODE: 0\r\n'---后面查詢到相關(guān)信息俯邓,開(kāi)啟手機(jī)相關(guān)的權(quán)限參考:https://airtest.doc.io.netease.com/IDEdocs/device_connection/2_android_faq/#_6
我使用的是vivo手機(jī)骡楼,所以在設(shè)置了相關(guān)功能后依舊會(huì)提示,所以我將開(kāi)發(fā)者模式等等的開(kāi)啟稽鞭,接著把自己測(cè)試的apk權(quán)限全部開(kāi)啟鸟整,并且開(kāi)啟了自啟動(dòng),后續(xù)調(diào)試就正常了朦蕴,如果有報(bào)這個(gè)錯(cuò)誤的同學(xué)篮条,可以嘗試下把所有的權(quán)限都開(kāi)啟
20.pycharm運(yùn)行pytest一些問(wèn)題
一開(kāi)始進(jìn)行學(xué)習(xí)pytest調(diào)試時(shí),發(fā)現(xiàn)運(yùn)行時(shí)就報(bào)錯(cuò):
TypeError: `args` parameter expected to be a list of strings, got: '-q test_g1.py' (type: <class 'str'>)
if __name__ == '__main__':
pytest.main('-s test_g1.py')
之前的版本使用的是pytest.main('-s test_g1.py')吩抓,傳的是一個(gè)str涉茧,我本地的pytest6.0.1版本,所以需要傳的是List[str],所以修改如下:
if __name__ == '__main__':
pytest.main(["-s", "test_g1.py"])