分享

一种自动化UI测试方案

 F2967527 2022-01-21

1. 背景

现有解码库功能,已经配备了一套GUI测试软件,其中包含API接口测试,以及通过快捷键激活的可靠性测试,通过长时间定时打开、关闭多路视频,收集内存/CPU占用信息、分析日志中响应时间、警告和错误信息等,来检验解码库和对应平台后端的功能、性能、稳定性。

再利用clumsy这种网络干扰工具来模拟网络故障,就能更好地检验全线软件产品在网络边界条件下的容错、故障恢复及风险控制能力。

上述两种方法可归类到冒烟测试范畴,新版本软件一旦通过冒烟测试,就可以部署到试运行环境。

试运行环境中业务系统通过C/S架构的GUI与用户交互,鼠标打开设备时调用解码库API来获取其视频图像并绘制到窗口。除此以外,GUI系统还集成了众多独立库,功能广泛。

2. GUI系统的测试

测试GUI系统离不开人工,因为人工检验最容易开启、很多情况下也最靠谱,但往往变成只依靠人工,容易被忽视的是人工还是最低效的方式,首先她的时间成本、金钱成本最高,其次经过多轮迭代后,开发工程师容易对测试产生依赖,不再重视自测,同时重复劳动的乏味使得测试工程师难以获得成就认可、耐心几何下降、提高工作品质的诉求降低,于是内部信任度下降,协作逐渐困难。

如果根据GUI系统的特征,做一些针对性的自动化测试,可以一定程度上避免上述乱象、缩短发布周期,继而更好地保障计划和质量。

拿计算器软件作例,如果依赖人工点击验证,每次发布新版本都要用不同参数来输入各种表达式去计算,同时还得先备好结果,再肉眼比较答案。一次测试预计花费15秒,每天全力以赴8小时检验1920个表达式,一周后工程师诊断出右手腱鞘炎,他敏捷地改用左手,下下周左手也被确诊,该死的表达式,他离职后产品经理听从测试经理的建议,重新修订发布计划。

好吧,这是个笑话,计算器当然是很容易实施自动化UI测试的,比如模拟键盘输入、读取输入框表达式、计算结果、比较。。。惨案还有避免的可能。

可惜大多数GUI系统不容易实施自动化测试,比如输入键鼠后某块区域UI上内容变化,这得依赖UI框架的特性,Web领域有一些高效解决方案,能拿到DOM树的变化结果即可校验,那么C/S架构的GUI系统呢?

3. 调研

自动化GUI测试是个古老的话题,有过很多方案,调研之初直接忽略了类似AutoIt的自定义脚本类型,不如基于Python的pywinautopyautogui,容易扩展、跨平台,生命力更强。

pywinauto是个很棒的开源项目,大概是根据Windows上Spy++或Inspect.exe获取指定GUI软件的窗口、可视化组件的属性信息,然后用其API访问、控制这些可视化组件,从而实现测试。

官方example中以Notepad为例,比如:

app = Application(backend='uia').start('notepad.exe')app.UntitledNotepad.menu_select('File->SaveAs')app.SaveAs.ComboBox5.select('UTF-8')app.SaveAs.edit1.set_text('Example-utf8.txt')app.SaveAs.Save.click()

看上去和编写GUI实现代码类似,简直业界福音。

实际运用到业务系统中发现,UI组件不易分辨,点击某个按钮后弹出新窗口,UI组件关系更加复杂,如果从头针对性设计UI去适应Spy++或Inspect.exe应该可以实战运用,这又是TDD-测试驱动开发的一例佐证。

考虑到她仅仅支持Windows,遂跳到 pyautogui项目。

4. pyautogui实践

这是一个类似Autoit的方案,提供发送鼠标、键盘消息、屏幕快照的API,也能够像Sikulilackey一样,API根据指定图片、定位到GUI中对应位置,随后脚本移动鼠标实现控制,这样就实现了利用解码库,频繁打开和关闭设备的自动化测试。

最后测试脚本大概如下,定时在窗口左侧区域,逐个双击设备菜单项、打开视频,5秒后关闭视频,为避免打开设备失败时弹出对话框而无法继续测试,还会单击下对话框右上角的关闭位置。

import timeimport pyautogui
def printt(log): print('{}>{}'.format(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), log))
def click(x, y, clicks=2): pyautogui.moveTo(x, y, 0.5, pyautogui.easeInOutQuad) pyautogui.click(duration=0.5, clicks=clicks)
def play_device(x, y): click(x, y) time.sleep(0.02)    pyautogui.click(duration=0.5)
def close_device(x, y): click(x, y)
def do_test(idx, interval=1): play_positions = [ [120, 120], [120, 160], [120, 200], [120, 240], [120, 280], [120, 320], ] close_positions = [ [1900, 80], [1900, 80], [1900, 80], [1900, 80], [1900, 80], [1900, 80], ] # error notify dialog dialog_postions = [1120, 480]
for i in range(0, len(play_positions), 2): printt('test loop is {}'.format(1 + len(play_positions) * (idx-1) + i))
for j in range(2): click(dialog_postions[0], dialog_postions[1], clicks=2) play_device(play_positions[i + j][0], play_positions[i + j][1]) time.sleep(interval)
time.sleep(5 * interval)
for j in range(2): click(dialog_postions[0], dialog_postions[1], clicks=2) close_device(close_positions[i + j][0], close_positions[i + j][1]) time.sleep(interval)
if __name__ == '__main__': time.sleep(2)
idx = 1 while True: do_test(idx) idx = idx + 1

5. 思考

虽然每个基本功能有自己的测试方案,向最终用户交付使用的是集成了许多基本功能模块的GUI系统,面向GUI的测试可称为验收测试,她的特殊是因为各个基本功能多且依赖,变化频繁,导致自测不充分,流转到GUI来最终检验,实战中发生过太多单个功能自测很棒,验收测试却故障频频,而且问题难定位、定位后回归测试如果依赖人工的话发布周期拉长,导致多方焦虑,解决故障的效率也低。

所以不能忽视面向GUI的自动化测试,即使她也无法全面覆盖测试路径,实践中可选择重要的、合适的功能,最大化地提高效率。

Sikulilackey这样的基于图片识别定位的测试方案非常棒,完全不依赖于GUI的实现框架,轻松移植,接下来开发GUI过程中多去实践TDD、思考如何更方便地测试该项功能,即使视频这种看上去无法自动检验的功能,其实也是可以通过自动化测试来高效回归检验,比如在GUI中设计一个鲜明的UI区域,实时显示FPS/带宽/累计视频帧数/设备重启次数等数据,测试脚本再通过图片识别功能去提取这些数据,从而把看上去无法检验的视频功能也做到了定性、定量的测试。

行胜于言,如果感受到一项改善实践起来很难、甚至接受都很难,反倒可能说明她很适合你,越是困难的事情越要重复做,勇于脱离舒适区才能够剔除过去由于实践不足带来的不利因素。

6. 参考

  • awesome-test-automation

  • c#-test-automation

  • lackey

  • Sikuli

  • 网易的AirTest,Windows版基于PyWinAuto,手机版基于Sikuli和PoCo,PC上控制手机很方便

    • 其他项目上用还不错

      本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
      转藏 分享 献花(0

      0条评论

      发表

      请遵守用户 评论公约

      类似文章 更多