12实操 · 测试网易云音乐并爬取热歌榜的100首歌曲名称
同学们好,我是网易AirtestProject的团队成员晓娟,欢迎大家来到我们的视频教程专栏《14天Airtest自动化测试小白课程》。
今天我将跟同学们一起来完成1个稍微复杂一点的自动化测试脚本。今天的课程主要有2个目的,一个是将我们之前所学的课程知识串联起来,简单带大家复习一下;另一个是想跟大家分享一下在编写自动化测试脚本过程的一些思路。
那么在正式编写脚本之前呢,我们先来看看完成这个脚本需要有哪些准备和要求呢?
准备工作
首先我们需要准备1台测试设备(安卓、iOS、模拟器均可)。并且打开电脑上装好的IDE,用IDE连接上待测设备:
先用USB线将待测设备连接到电脑上,然后开启设备中的“USB调试”设置,点击设备连接窗口的“刷新ADB”,等刷出待测设备,并且设备状态为device时,我们就可以点击connect按钮连接上待测设备。
接着在测试机上安装好待测应用,网易云音乐。
最后在IDE上,新建1个.air脚本用于完成今天的测试任务。
做完这些准备工作之后,我们再来了解下今天这个脚本具体有哪些要求:
①要求在脚本中指定测试设备,并保存log内容到指定目录,方便后续生成报告
②要求录制脚本运行过程的视频,并保存到指定目录下
③要求运行完用例后自动生成测试报告,并且不论用例是否成功执行,最终都会生成测试报告方便查看原因
④用例一:进入网易云音乐app的首页
⑤用例二:找到指定的薛之谦的歌曲
⑥用例三:获取抖音排行榜所有歌曲的名称并打印出来
在app上的大致操作如下:

做好准备工作,并了解完脚本需求之后,我们就可以着手进入脚本编写环节啦。
脚本编写
刚才新建.air脚本的时候,编辑器会自动帮我们补充一些初始化语句,比如__author__是用于在报告中显示作者信息的,另外我们可以用__desc__ = """ """,描述脚本的内容信息。

举个例子,我们可以把刚才提到的一些脚本要求,放到这个详细信息里面,之后我们就可以在测试报告中看到这段脚本的描述信息:
__desc__ = """
网易云音乐app-测试实操
1.录制运行视频、用例跑完后自动生成报告
2.进入网易云音乐首页
3.找到薛之谦的指定歌曲
4.获取抖音排行榜的所有歌名
"""
然后from airtest.core.api import *,是引入airtest的核心API。
auto_setup(__file__)是脚本初始化接口,在这里我们可以指定设备参数和log内容的保存路径。
# 脚本初始化
auto_setup(__file__,devices=["android://127.0.0.1:5037/PFT4PBLF75GQHYBM"],logdir=r"D:\test\pro01_log")
这次我们用到的是1台序列号为xxxx的真机设备,并且我们指定log内容的保存路径为"D:\test\pro01_log"。
录制运行过程的视频,我们使用的是start_recording()方法,查看这个方法的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),可以看到这个方法属于Recorder类,所以我们需要实例化1个Recorder类来调用这个方法,并且实例化Recorder类还需要传入1个adb参数:
adb = ADB(serialno="PFT4PBLF75GQHYBM")
recorder = Recorder(adb)
recorder.start_recording()
注意别忘了把adb和recorder的import写到脚本的最前面。脚本的最后需要结束录屏,用的是stop_recording()。并且可以指定录屏文件的保存路径,我们就把它放到刚才的log路径里面去,命名为cloudmusic.mp4:
recorder.stop_recording(output=r"D:\test\pro01_log\cloudmusic.mp4")
写完录屏脚本,我们开始来写用例的脚本,第一个用例是进入网易云音乐的首页,为了保证进入时初始状态一致,我们可以使用clear_app(),清除应用的数据,再使用start_app()打开目标应用,这俩个方法传入的参数都是应用的包名。
clear_app("com.netease.cloudmusic")
start_app("com.netease.cloudmusic")
IDE的安卓助手可以帮助我们快速查看当前应用的包名(在包名列表中ctrl+c,可以复制应用包名到剪切板,然后ctrl+v复制到我们的脚本上):

这样打开网易云音乐以后,会让我们选择是否同意它的服务条款,我们可以两个选项都测一下,先点击不同意并退出app的按钮,这里的点击,我们既可以选择airtest的touch(截图),也可以选择poco的控件精确点击,这两者是可以随意混用,相互补充的。
这里我们使用poco的控件点击,先在poco辅助窗选择Android模式,选择插入初始化脚本,并且把poco的初始化脚本放到打开app的脚本之后。然后等待刷出UI树,点击控件检索按钮,检索我们的目标控件(这里我们可以双击节点生成控件定位脚本,然后我们自行补充点击操作,如果想查看运行情况,可以选中该脚本右键单独运行):

这里点击不同意并退出app的按钮,我们还可以做一个断言,当这个服务条款的页面消失了,就说明我们已经点击完控件了,所以断言可以这么写:

然后我们可以重新打开app,测一下点击同意的按钮以及完成后续进入APP的操作。大致流程是同意一系列的权限之后,我们就可以进入网易云音乐的首页了。
这里需要注意的是,为了避免页面切换过程中,控件还没加载出来我们就进行了点击操作,在连续的点击操作之间,我们既可以使用sleep等待,也可以使用等待元素出现的方法,wait_for_appearance(timeout=60)。
然后第2个用例是找到薛之谦的指定歌曲。点击右上角的搜索控件,然后输入关键词:薛之谦。这里我们既可以用poco的set_text进入输入,也可以用airtest的text接口输入。这里以text接口输入为例(text接口会自带1个回车,如果不需要,可以把enter的值改成False):

接着我们点击薛之谦的歌单,进入到他的歌曲主页。然后点击近期热门开始播放,左右滑动底下的歌曲列表可以切换不同的歌曲,目标歌曲是薛之谦的《其实》。可以看到:我们大概需要左滑5、6次,才能找到这首目标歌曲。
所以我们可以写一个脚本逻辑,重复左滑切换歌曲的动作,直到出现了目标歌曲才停止:
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("已找到目标歌曲")
sleep(1.0)
break
其中,poco左滑操作,我们可以在选项设置中,把实时坐标和相对坐标都勾选上,此时鼠标移动到设备的画面上,就会实时显示出来1个相对坐标,poco用的正是相对坐标系。右键单击设备画面,可以帮助我们把坐标复制到剪切板,之后我们可以在脚本中粘贴使用。(airtest用的是绝对坐标,在设置中仅勾选实时坐标即可)。
找到目标歌曲之后,我们还可以点击进入歌曲详情页,进行一些“喜欢操作”啥的。
最后1个用例是获取抖音排行榜的所有歌曲名称,因为抖音排行榜的入口在app首页,所以我们要从这首歌曲的详情页中,返回到首页。过程需要点击4次返回的按钮,同学们可以用截图点击,也可以用poco定位这个返回的控件,然后进行点击。但对于安卓设备,有一个更加简洁好用的方法,那就是keyevent("back"),可以实现返回上层页面的操作。我们只要写个简单的循环执行操作即可:
for i in range(4):
keyevent("BACK")
回到首页之后,我们再来研究最后1个用例。点击首页的排行榜,然后从下往上滑动几次,找到特色排行榜的抖音排行榜,这个脚本的逻辑跟刚才不断向左滑动找薛之谦的指定歌曲有点类似,只不过之前是一直向左滑动,现在是执行多次的从下往上滑动直到找到抖音排行榜:
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
之后点击抖音排行榜,进入抖音排行榜的歌曲列表页面。要想获取抖音排行榜其中一首歌曲的名称,其实很简单,我们可以先定位到歌曲名称的控件,然后利用poco的get_text(),获取歌曲名称。
而获取抖音排行榜里面所有歌曲名称,难点有以下几个:
1.当前页面并不会显示出所有歌曲的名称控件,所以我们要一边滑动列表,一遍等待新的歌曲名称控件出现,我们再获取;
2.滑动歌曲列表时,我们不能保证当前页面的所有歌曲名称,都刚好是我们未获取的名称,所以我们需要判断下,哪些歌曲是已经获取了,哪些是未获取的
3.我们需要观察获取歌曲名称的控件,是否有一定的规律可循,或者说,能否通过节点遍历,不断定位到新的歌曲名称的控件
4.我们如何判断歌曲名称已经获取完毕
看到这里,同学们可以暂停一下视频,然后看看如果是自己编写这个脚本,将会如何实现。
好了,思考过后,就一起来看下我们是如何实现这个需求的:

可以看到,这里设置了1个循环,获取完当页的歌曲名称控件之后,就执行滑动操作,然后循环获取下一个页面的歌曲名称控件。观察UI树的结构,我们不难判断出,这里可以利用poco的遍历节点,来遍历我们的歌曲名称控件。

至于如何不获取到重复的控件,我们定义了一个空的列表用于存放歌曲名称,每次获取到1个新的歌曲名称,就会被存放到这个列表中去,并且仅当最新获取的那个歌曲名称,不在当前列表中,我们才会进行存放操作,并把名称打印出来,这样就避免了获取到重复的歌曲名称。
最后1个问题,如何判定我们已经获取了排行榜里面的所有歌曲名称:这里我们我们利用的是列表的长度是否还在变化作为考量标准,因为列表里面存放的是不断新增的歌曲名称,如果列表不再新增歌曲名称,也就是列表的长度不再发生变化时,我们可以认为,已经获取完毕了。
至此,3个用例都编写完毕了。我们可以在脚本末尾使用simple_report(),生成该脚本的运行报告,并且为了避免用例运行失败,而导致最后没有运行到这个生成报告的语句,我们可以使用try-finally,把执行用例的脚本放在try里面,而生成报告的语句放到finally后面,意思是不论前面脚本是否执行成功,最后都要给我们生成1个测试报告,供我们查找问题。
完整的脚本运行情况如下:
查看测试报告
因为这里我们使用simple_report()接口生成报告,并且制定了导出路径为"D:\test\pro01_log\log.html",所以在IDE运行脚本以后,我们就可以到指定的路径去查看生成的测试报告了:

小结
好了,今天的教程到这里就差不多要结束了。今天带着大家完成了1个完整的且相对复杂一些的脚本,其中我们提到了很多细节的技巧和知识点。同学们课后找到自己没消化的那部分内容,反复观看,并且多尝试自己动手编写脚本,相信不久之后,同学们就可以独立编写出更多复杂用例的自动化脚本了。另外,需要课堂上的示例脚本来参考的同学,请移步我们的官方公众号,回复“A12”获取这节课的测试脚本。
下节课我们会跟大家聊一下,1个企业级的自动化解决方案是如何做的,下期不见不散啦~
附上脚本截图:





