12實(shí)操 · 測(cè)試網(wǎng)易云音樂(lè)并爬取熱歌榜的100首歌曲名稱(chēng)
同學(xué)們好,我是網(wǎng)易AirtestProject的團(tuán)隊(duì)成員曉娟,歡迎大家來(lái)到我們的視頻教程專(zhuān)欄《14天Airtest自動(dòng)化測(cè)試小白課程》。
今天我將跟同學(xué)們一起來(lái)完成1個(gè)稍微復(fù)雜一點(diǎn)的自動(dòng)化測(cè)試腳本。今天的課程主要有2個(gè)目的,一個(gè)是將我們之前所學(xué)的課程知識(shí)串聯(lián)起來(lái),簡(jiǎn)單帶大家復(fù)習(xí)一下;另一個(gè)是想跟大家分享一下在編寫(xiě)自動(dòng)化測(cè)試腳本過(guò)程的一些思路。
那么在正式編寫(xiě)腳本之前呢,我們先來(lái)看看完成這個(gè)腳本需要有哪些準(zhǔn)備和要求呢?
準(zhǔn)備工作
首先我們需要準(zhǔn)備1臺(tái)測(cè)試設(shè)備(安卓、iOS、模擬器均可)。并且打開(kāi)電腦上裝好的IDE,用IDE連接上待測(cè)設(shè)備:
先用USB線將待測(cè)設(shè)備連接到電腦上,然后開(kāi)啟設(shè)備中的“USB調(diào)試”設(shè)置,點(diǎn)擊設(shè)備連接窗口的“刷新ADB”,等刷出待測(cè)設(shè)備,并且設(shè)備狀態(tài)為device時(shí),我們就可以點(diǎn)擊connect按鈕連接上待測(cè)設(shè)備。
接著在測(cè)試機(jī)上安裝好待測(cè)應(yīng)用,網(wǎng)易云音樂(lè)。
最后在IDE上,新建1個(gè).air腳本用于完成今天的測(cè)試任務(wù)。
做完這些準(zhǔn)備工作之后,我們?cè)賮?lái)了解下今天這個(gè)腳本具體有哪些要求:
①要求在腳本中指定測(cè)試設(shè)備,并保存log內(nèi)容到指定目錄,方便后續(xù)生成報(bào)告
②要求錄制腳本運(yùn)行過(guò)程的視頻,并保存到指定目錄下
③要求運(yùn)行完用例后自動(dòng)生成測(cè)試報(bào)告,并且不論用例是否成功執(zhí)行,最終都會(huì)生成測(cè)試報(bào)告方便查看原因
④用例一:進(jìn)入網(wǎng)易云音樂(lè)app的首頁(yè)
⑤用例二:找到指定的薛之謙的歌曲
⑥用例三:獲取抖音排行榜所有歌曲的名稱(chēng)并打印出來(lái)
在app上的大致操作如下:

做好準(zhǔn)備工作,并了解完腳本需求之后,我們就可以著手進(jìn)入腳本編寫(xiě)環(huán)節(jié)啦。
腳本編寫(xiě)
剛才新建.air腳本的時(shí)候,編輯器會(huì)自動(dòng)幫我們補(bǔ)充一些初始化語(yǔ)句,比如__author__是用于在報(bào)告中顯示作者信息的,另外我們可以用__desc__ = """ ?""",描述腳本的內(nèi)容信息。

舉個(gè)例子,我們可以把剛才提到的一些腳本要求,放到這個(gè)詳細(xì)信息里面,之后我們就可以在測(cè)試報(bào)告中看到這段腳本的描述信息:
__desc__ = """
網(wǎng)易云音樂(lè)app-測(cè)試實(shí)操
1.錄制運(yùn)行視頻、用例跑完后自動(dòng)生成報(bào)告
2.進(jìn)入網(wǎng)易云音樂(lè)首頁(yè)
3.找到薛之謙的指定歌曲
4.獲取抖音排行榜的所有歌名
"""
然后from airtest.core.api import *,是引入airtest的核心API。
auto_setup(__file__)是腳本初始化接口,在這里我們可以指定設(shè)備參數(shù)和log內(nèi)容的保存路徑。
# 腳本初始化
auto_setup(__file__,devices=["android://127.0.0.1:5037/PFT4PBLF75GQHYBM"],logdir=r"D:\test\pro01_log")
這次我們用到的是1臺(tái)序列號(hào)為xxxx的真機(jī)設(shè)備,并且我們指定log內(nèi)容的保存路徑為"D:\test\pro01_log"。
錄制運(yùn)行過(guò)程的視頻,我們使用的是start_recording()方法,查看這個(gè)方法的api(https://airtest.readthedocs.io/zh_CN/latest/all_module/airtest.core.android.recorder.html?highlight=start_recording#airtest.core.android.recorder.Recorder.start_recording),可以看到這個(gè)方法屬于Recorder類(lèi),所以我們需要實(shí)例化1個(gè)Recorder類(lèi)來(lái)調(diào)用這個(gè)方法,并且實(shí)例化Recorder類(lèi)還需要傳入1個(gè)adb參數(shù):
adb = ADB(serialno="PFT4PBLF75GQHYBM")
recorder = Recorder(adb)
recorder.start_recording()
注意別忘了把a(bǔ)db和recorder的import寫(xiě)到腳本的最前面。腳本的最后需要結(jié)束錄屏,用的是stop_recording()。并且可以指定錄屏文件的保存路徑,我們就把它放到剛才的log路徑里面去,命名為cloudmusic.mp4:
recorder.stop_recording(output=r"D:\test\pro01_log\cloudmusic.mp4")
寫(xiě)完錄屏腳本,我們開(kāi)始來(lái)寫(xiě)用例的腳本,第一個(gè)用例是進(jìn)入網(wǎng)易云音樂(lè)的首頁(yè),為了保證進(jìn)入時(shí)初始狀態(tài)一致,我們可以使用clear_app(),清除應(yīng)用的數(shù)據(jù),再使用start_app()打開(kāi)目標(biāo)應(yīng)用,這倆個(gè)方法傳入的參數(shù)都是應(yīng)用的包名。
clear_app("com.netease.cloudmusic")
start_app("com.netease.cloudmusic")
IDE的安卓助手可以幫助我們快速查看當(dāng)前應(yīng)用的包名(在包名列表中ctrl+c,可以復(fù)制應(yīng)用包名到剪切板,然后ctrl+v復(fù)制到我們的腳本上):

這樣打開(kāi)網(wǎng)易云音樂(lè)以后,會(huì)讓我們選擇是否同意它的服務(wù)條款,我們可以?xún)蓚€(gè)選項(xiàng)都測(cè)一下,先點(diǎn)擊不同意并退出app的按鈕,這里的點(diǎn)擊,我們既可以選擇airtest的touch(截圖),也可以選擇poco的控件精確點(diǎn)擊,這兩者是可以隨意混用,相互補(bǔ)充的。
這里我們使用poco的控件點(diǎn)擊,先在poco輔助窗選擇Android模式,選擇插入初始化腳本,并且把poco的初始化腳本放到打開(kāi)app的腳本之后。然后等待刷出UI樹(shù),點(diǎn)擊控件檢索按鈕,檢索我們的目標(biāo)控件(這里我們可以雙擊節(jié)點(diǎn)生成控件定位腳本,然后我們自行補(bǔ)充點(diǎn)擊操作,如果想查看運(yùn)行情況,可以選中該腳本右鍵單獨(dú)運(yùn)行):

這里點(diǎn)擊不同意并退出app的按鈕,我們還可以做一個(gè)斷言,當(dāng)這個(gè)服務(wù)條款的頁(yè)面消失了,就說(shuō)明我們已經(jīng)點(diǎn)擊完控件了,所以斷言可以這么寫(xiě):

然后我們可以重新打開(kāi)app,測(cè)一下點(diǎn)擊同意的按鈕以及完成后續(xù)進(jìn)入APP的操作。大致流程是同意一系列的權(quán)限之后,我們就可以進(jìn)入網(wǎng)易云音樂(lè)的首頁(yè)了。
這里需要注意的是,為了避免頁(yè)面切換過(guò)程中,控件還沒(méi)加載出來(lái)我們就進(jìn)行了點(diǎn)擊操作,在連續(xù)的點(diǎn)擊操作之間,我們既可以使用sleep等待,也可以使用等待元素出現(xiàn)的方法,wait_for_appearance(timeout=60)。
然后第2個(gè)用例是找到薛之謙的指定歌曲。點(diǎn)擊右上角的搜索控件,然后輸入關(guān)鍵詞:薛之謙。這里我們既可以用poco的set_text進(jìn)入輸入,也可以用airtest的text接口輸入。這里以text接口輸入為例(text接口會(huì)自帶1個(gè)回車(chē),如果不需要,可以把enter的值改成False):

接著我們點(diǎn)擊薛之謙的歌單,進(jìn)入到他的歌曲主頁(yè)。然后點(diǎn)擊近期熱門(mén)開(kāi)始播放,左右滑動(dòng)底下的歌曲列表可以切換不同的歌曲,目標(biāo)歌曲是薛之謙的《其實(shí)》??梢钥吹剑何覀兇蟾判枰蠡?、6次,才能找到這首目標(biāo)歌曲。
所以我們可以寫(xiě)一個(gè)腳本邏輯,重復(fù)左滑切換歌曲的動(dòng)作,直到出現(xiàn)了目標(biāo)歌曲才停止:
while True:
if not exists(Template(r"tpl1604893064017.png", threshold=0.85, record_pos=(-0.342, 0.818), resolution=(720, 1440))):
poco.swipe([0.719, 0.907],[0.246, 0.908])
else:
print("已找到目標(biāo)歌曲")
sleep(1.0)
break
其中,poco左滑操作,我們可以在選項(xiàng)設(shè)置中,把實(shí)時(shí)坐標(biāo)和相對(duì)坐標(biāo)都勾選上,此時(shí)鼠標(biāo)移動(dòng)到設(shè)備的畫(huà)面上,就會(huì)實(shí)時(shí)顯示出來(lái)1個(gè)相對(duì)坐標(biāo),poco用的正是相對(duì)坐標(biāo)系。右鍵單擊設(shè)備畫(huà)面,可以幫助我們把坐標(biāo)復(fù)制到剪切板,之后我們可以在腳本中粘貼使用。(airtest用的是絕對(duì)坐標(biāo),在設(shè)置中僅勾選實(shí)時(shí)坐標(biāo)即可)。
找到目標(biāo)歌曲之后,我們還可以點(diǎn)擊進(jìn)入歌曲詳情頁(yè),進(jìn)行一些“喜歡操作”啥的。
最后1個(gè)用例是獲取抖音排行榜的所有歌曲名稱(chēng),因?yàn)槎兑襞判邪竦娜肟谠赼pp首頁(yè),所以我們要從這首歌曲的詳情頁(yè)中,返回到首頁(yè)。過(guò)程需要點(diǎn)擊4次返回的按鈕,同學(xué)們可以用截圖點(diǎn)擊,也可以用poco定位這個(gè)返回的控件,然后進(jìn)行點(diǎn)擊。但對(duì)于安卓設(shè)備,有一個(gè)更加簡(jiǎn)潔好用的方法,那就是keyevent("back"),可以實(shí)現(xiàn)返回上層頁(yè)面的操作。我們只要寫(xiě)個(gè)簡(jiǎn)單的循環(huán)執(zhí)行操作即可:
for i in range(4):
keyevent("BACK")
回到首頁(yè)之后,我們?cè)賮?lái)研究最后1個(gè)用例。點(diǎn)擊首頁(yè)的排行榜,然后從下往上滑動(dòng)幾次,找到特色排行榜的抖音排行榜,這個(gè)腳本的邏輯跟剛才不斷向左滑動(dòng)找薛之謙的指定歌曲有點(diǎn)類(lèi)似,只不過(guò)之前是一直向左滑動(dòng),現(xiàn)在是執(zhí)行多次的從下往上滑動(dòng)直到找到抖音排行榜:
while True:
if not exists(Template(r"tpl1604894647838.png", record_pos=(-0.318, 0.122), resolution=(720, 1440))):
poco.swipe([0.531, 0.733],[0.549, 0.225])
else:
print("已找到抖音排行榜")
sleep(1.0)
break
之后點(diǎn)擊抖音排行榜,進(jìn)入抖音排行榜的歌曲列表頁(yè)面。要想獲取抖音排行榜其中一首歌曲的名稱(chēng),其實(shí)很簡(jiǎn)單,我們可以先定位到歌曲名稱(chēng)的控件,然后利用poco的get_text(),獲取歌曲名稱(chēng)。
而獲取抖音排行榜里面所有歌曲名稱(chēng),難點(diǎn)有以下幾個(gè):
1.當(dāng)前頁(yè)面并不會(huì)顯示出所有歌曲的名稱(chēng)控件,所以我們要一邊滑動(dòng)列表,一遍等待新的歌曲名稱(chēng)控件出現(xiàn),我們?cè)佾@??;
2.滑動(dòng)歌曲列表時(shí),我們不能保證當(dāng)前頁(yè)面的所有歌曲名稱(chēng),都剛好是我們未獲取的名稱(chēng),所以我們需要判斷下,哪些歌曲是已經(jīng)獲取了,哪些是未獲取的
3.我們需要觀察獲取歌曲名稱(chēng)的控件,是否有一定的規(guī)律可循,或者說(shuō),能否通過(guò)節(jié)點(diǎn)遍歷,不斷定位到新的歌曲名稱(chēng)的控件
4.我們?nèi)绾闻袛喔枨Q(chēng)已經(jīng)獲取完畢
看到這里,同學(xué)們可以暫停一下視頻,然后看看如果是自己編寫(xiě)這個(gè)腳本,將會(huì)如何實(shí)現(xiàn)。
好了,思考過(guò)后,就一起來(lái)看下我們是如何實(shí)現(xiàn)這個(gè)需求的:

可以看到,這里設(shè)置了1個(gè)循環(huán),獲取完當(dāng)頁(yè)的歌曲名稱(chēng)控件之后,就執(zhí)行滑動(dòng)操作,然后循環(huán)獲取下一個(gè)頁(yè)面的歌曲名稱(chēng)控件。觀察UI樹(shù)的結(jié)構(gòu),我們不難判斷出,這里可以利用poco的遍歷節(jié)點(diǎn),來(lái)遍歷我們的歌曲名稱(chēng)控件。

至于如何不獲取到重復(fù)的控件,我們定義了一個(gè)空的列表用于存放歌曲名稱(chēng),每次獲取到1個(gè)新的歌曲名稱(chēng),就會(huì)被存放到這個(gè)列表中去,并且僅當(dāng)最新獲取的那個(gè)歌曲名稱(chēng),不在當(dāng)前列表中,我們才會(huì)進(jìn)行存放操作,并把名稱(chēng)打印出來(lái),這樣就避免了獲取到重復(fù)的歌曲名稱(chēng)。
最后1個(gè)問(wèn)題,如何判定我們已經(jīng)獲取了排行榜里面的所有歌曲名稱(chēng):這里我們我們利用的是列表的長(zhǎng)度是否還在變化作為考量標(biāo)準(zhǔn),因?yàn)榱斜砝锩娲娣诺氖遣粩嘈略龅母枨Q(chēng),如果列表不再新增歌曲名稱(chēng),也就是列表的長(zhǎng)度不再發(fā)生變化時(shí),我們可以認(rèn)為,已經(jīng)獲取完畢了。
至此,3個(gè)用例都編寫(xiě)完畢了。我們可以在腳本末尾使用simple_report(),生成該腳本的運(yùn)行報(bào)告,并且為了避免用例運(yùn)行失敗,而導(dǎo)致最后沒(méi)有運(yùn)行到這個(gè)生成報(bào)告的語(yǔ)句,我們可以使用try-finally,把執(zhí)行用例的腳本放在try里面,而生成報(bào)告的語(yǔ)句放到finally后面,意思是不論前面腳本是否執(zhí)行成功,最后都要給我們生成1個(gè)測(cè)試報(bào)告,供我們查找問(wèn)題。
完整的腳本運(yùn)行情況如下:
查看測(cè)試報(bào)告
因?yàn)檫@里我們使用simple_report()接口生成報(bào)告,并且制定了導(dǎo)出路徑為"D:\test\pro01_log\log.html",所以在IDE運(yùn)行腳本以后,我們就可以到指定的路徑去查看生成的測(cè)試報(bào)告了:

小結(jié)
好了,今天的教程到這里就差不多要結(jié)束了。今天帶著大家完成了1個(gè)完整的且相對(duì)復(fù)雜一些的腳本,其中我們提到了很多細(xì)節(jié)的技巧和知識(shí)點(diǎn)。同學(xué)們課后找到自己沒(méi)消化的那部分內(nèi)容,反復(fù)觀看,并且多嘗試自己動(dòng)手編寫(xiě)腳本,相信不久之后,同學(xué)們就可以獨(dú)立編寫(xiě)出更多復(fù)雜用例的自動(dòng)化腳本了。另外,需要課堂上的示例腳本來(lái)參考的同學(xué),請(qǐng)移步我們的官方公眾號(hào),回復(fù)“A12”獲取這節(jié)課的測(cè)試腳本。
下節(jié)課我們會(huì)跟大家聊一下,1個(gè)企業(yè)級(jí)的自動(dòng)化解決方案是如何做的,下期不見(jiàn)不散啦~
附上腳本截圖:



