模拟器客户端自动化测试实践

news/2024/11/15 5:29:54/

1 背景

1.1 作者背景

作者在过去一年多内,在某大型游戏公司负责游戏 SDK 业务的质量保障工作,在客户端自动化测试方面有一定的探索和实践。

1.2 项目背景

1.2.1 为什么要做客户端自动化测试

相信这个问题的答案,大家心里都有答案。有人说是:发现 bug,验证功能;有人说是:降低回归测试成本;有人说是:提高测试能力;不一而足。

对于 SDK 业务而言,无论是登录还是支付,稳定性要求都非常高,不允许出现失误。软件的变动(包括客户端和服务端)是否会影响功能,如果能在客户端层面进行自动化的确认,不论对质量,还是效率,都有很重要的意义。

除了测试能力对开发服务开放以外,我们还需要对运维服务。

自 2020 年以来,世界经济都在下行,各家公司整体都在降本增效,以应对经济下行的压力。与此同时,微服务和多中间件架构已经成为了各家公司的标配,这些中间件和服务器平时实际使用率并不高,存在优化的空间。公司倾向于缩减硬件成本,运维也有 KPI 要求。对于测试人员的影响就是:服务端和中间件的硬件变动需要一定的回归测试来验证。在我们团队中,这种情况不是一次两次,而是在整个降本周期内一直存在。

1.2.2 为什么能做客户端自动化测试

任何一个决策,都应该考虑可行性,客户端自动化测试也不例外。

根据测试金字塔理论,客户端自动化测试因为以下几个问题,导致其性价比不如集成测试。

  • 1.UI 层变化太大,测试用例需要频繁改动,维护成本较高;

  • 2.集成度最高,难以准确定位故障;

  • 3.用真实设备来实施测试,实施成本最高;

  • 4.客户端的进程过多,导致其测试以外的干扰因素过多,因此稳定性较差。

因为业务独特的情况,这几个问题均可得到一定程度的削弱,可行性反而较高:

  • 1.游戏 SDK 有对应的客户端 DEMO,UI 层变化很小,即使变化往往也是 UI 树增加子节点,不影响定位原来的元素。

  • 2.由于游戏 SDK 的核心功能只有登录和支付,业务链路单一且功能相对平行,不会有太多的业务耦合,故障定位比较简单。举个栗子:支付宝支付挂了,要么是支付宝支付链路上的问题,不会影响微信支付的用例,除非是支付底层逻辑问题。退一步讲,上述情况发生了,定位也是比较容易的。

  • 3.无论是 Android Studio 还是 Xcode,其实都是支持模拟器的,我们可以借助模拟器来做测试,可以极大地降低硬件成本。可行原因参考本文:2.关于使用模拟器做自动化测试的调研。

  • 4.由于创建的模拟器没有安装太多应用,应用之间的干扰比较小。且模拟器在电脑中,网络环境比较好。

1.3 常见误区

1.3.1 期望客户端自动化测试能够测试出兼容性问题

客户端自动化测试不等于兼容性测试,不需要测试各种机型,各种屏幕的设备。那是兼容性测试的工作,自动化测试的核心还是降低测试执行成本,确认质量符合要求。

1.3.2 期望测试用例稳定性到 100%,不能存在误报

任何测试都会存在干扰因素,不可能 100% 稳定:硬件设备断电,网络不稳定,进程卡死,服务端测试环境故障,服务端和客户端配置错误,测试框架出 Bug,甚至真机设备上的系统弹窗都会导致测试用例失败。误报确实是衡量客户端测试有效性的重要因素,但不应该成为衡量的唯一因素。

我们应该秉持的态度是:一方面在误报出现时,尽快由测试人员介入确认该误报;另一方面不断完善测试框架,尽可能消除这些干扰因素。

1.4 技术栈选型

团队成员对 Python 和 Airtest 比较熟悉,并且之前有成熟的测试项目。Pytest 有丰富的测试插件,开发效率较高。此外可以自己编写钩子,本测试框架在配置测试环境、生成测试报告、发送测试邮件等环节多次用到了钩子。为了快速出成果,故选用了 Pytest + Airtest。

此外,为了紧跟前沿测试,使用了行为驱动开发的思想。测试驱动开发的思想,想必大家都有所耳闻。行为驱动开发,其实是前者的一种升级。应用在测试领域,就是行为驱动测试,它定义了每个测试步骤,并赋予了这些测试步骤一定的代码,使其能够实现这个测试步骤。如果只做到这些,那还不够诱人,行为驱动测试允许测试步骤的复用。具体示例请参考本文:4.1 测试脚本

Behavior-driven development is an extension of test-driven development, a development process that makes use of a simple DSL. These DSLs convert structured natural language statements into executable tests. The result is a closer relationship to acceptance criteria for a given function and the tests used to validate that functionality. As such it is a natural extension of TDD testing in general.

From Wikipedia: Behavior-driven development

2 关于使用模拟器做自动化测试的调研

正如前文所说,可以创建客户端模拟器用于客户端自动化测试,可以降低硬件成本。对于这一设想的可行性,作者花了较多时间做了深度调研,打通了模拟器客户端自动化测试的各个环节。

2.1 创建模拟器

对于安卓设备有:

# 切换到Android sdk的根目录cd /Users/<username>/Library/Android/sdk# 创建模拟器./tools/bin/avdmanager create avd -n <emulator_name> -k "system-images;android-22;google_apis;arm64-v8a"# 重命名模拟器./tools/bin/avdmanager move avd -n <emulator_name> <new_emulator_name># 删除模拟器./tools/bin/avdmanager delete avd -n <emulator_name># 启动模拟器./emulator/emulator -avd Pixel_5_API_22 & bg# 关闭模拟器kill `ps aux | grep Android/sdk/emulator/qemu | xargs | awk -F ' ' 'END{print $2}'`

对于iOS设备:

# 创建模拟器,机型iPhone-13-Pro-Max,系统版本iOS-15-2xcrun simctl create my_iphone_pro_max com.apple.CoreSimulator.SimDeviceType.iPhone-13-Pro-Max com.apple.CoreSimulator.SimRuntime.iOS-15-2# 删除模拟器xcrun simctl delete my_iphone_pro_max# 启动模拟器xcrun simctl boot my_iphone_pro_max# 关闭模拟器xcrun simctl shutdown my_iphone_pro_max# 检查设备的运行状态xcrun simctl bootstatus my_iphone_pro_max# 找到正在启动的模拟器设备xcrun simctl list | grep Booted# 给设备起一个别名xcrun simctl 7CD4F140-0BDC-4AE9-8FA0-960D33055044 rename my_iphone_pro_max

simctl提供了更为丰富的 api,可以实现更多的功能。

使用 Python 代码封装上述的命令,即是整个框架的底层工具包。

2.2 连接模拟器

连接模拟器的教程,网易的 Airtest 文档朱玉在前,我就不做赘述,请参考:

如何在 iOS 手机上进行自动化测试

如何在 Android 手机上进行自动化测试(上)

如何在 Android 手机上进行自动化测试(下)

# Tiffany.pyfrom poco.drivers.ios import iosPocofrom poco.drivers.android.uiautomation import AndroidUiautomationPocofrom airtest.core.api import auto_setup, connect_devicefrom cases.utils import config_parserclass Tiffany(object):"""底层工具箱"""def __init__(self, app_name: str) -> None:self.app_name = app_namedef make_android_poco(self) -> None:auto_setup(__file__)connect_device(uri=config_parser.get("DEVICE", "EMULATOR"))self.poco = AndroidUiautomationPoco(pre_action_wait_for_appearance=3,action_interval=0.5,poll_interval=0.4)def make_ios_poco(self) -> None:auto_setup(__file__, devices=[config_parser.get("DEVICE", "IPHONE_URI"), ]connect_device(config_parser.get("DEVICE", "IPHONE_URI"))self.poco = iosPoco(pre_action_wait_for_appearance=4,action_interval=0.1,poll_interval=0.2)

2.3 操作模拟器

对于 Android 设备,有祖传的 adb 命令来操作:

# 列出连接的设备./platform-tools/adb devices# 列出app./platform-tools/adb -s Pixel_5_API_22 shell pm list packages# 安装测试应用./platform-tools/adb -s Pixel_5_API_22 install <path_to_apk>.apk# 启动pocoservice./platform-tools/adb shell am start -n com.netease.open.pocoservice/com.netease.open.pocoservice.TestActivity# 启动测试应用./platform-tools/adb -s Pixel_5_API_22 shell am start com.example.sdk.demo.activity.SplashActivity# 卸载测试应用./platform-tools/adb -s Pixel_5_API_22 uninstall com.example.sdk.demo

对于 iOS 设备,有xcrun simctl命令来操作:

# 安装应用到指定的设备xcrun simctl install my_iphone_pro_max resources/packages/IOSDemoMainland.app# 卸载指定应用,根据bundle_idxcrun simctl uninstall my_iphone_pro_max com.netease.boltrend.sdk.demo# 列出已安装的应用xcrun simctl listapps my_iphone_pro_max# 查看app信息xcrun simctl appinfo my_iphone_pro_max com.apple.mobilesafari# 启动appxcrun simctl launch my_iphone_pro_max com.apple.mobilesafari# 关闭appxcrun simctl terminate my_iphone_pro_max com.apple.mobilesafari# 打开urlxcrun simctl openurl my_iphone_pro_max https://cn.bing.com/# Sure, you can use the global macOS shortcut to capture a screenshot of a simulator (⇧⌘4), but if you intend to use those for your App Store listing, you’re much better off using the io subcommand:$ xcrun simctl io booted screenshot app-screenshot.png# You can also use this subcommand to capture a video as you interact with your app from the simulator (or don’t, in the case of automated UI tests):$ xcrun simctl io booted recordVideo app-preview.mp4

此外,tidevice 也是好用的工具。详细用法请参考:阿里Python自动化工具 tidevice 使用指南

3 总体设计

总体设计架构如下:

4 详细设计

4.1 测试脚本

下面重点讲讲测试脚本编写:

4.1.1 定义测试步骤

针对一个邮箱登录的测试场景,可以用 gherkin 语言定义下面的测试步骤:

# mail_login_test.featureFeature: mail_login_testtestcases about mail login  Scenario: mail login with correct password    Given start app and initialize settings    And open the main board    When I admit agreements on login board    And I go to the mail login board    And I input correct password and login    Then assert login success

4.1.2 编写测试代码

每个测试步骤,都有对应的

# mail_login_test.pyfrom pytest_bdd import given, then, when@given('start app and initialize settings')def start_app_and_initialize_settings() -> None:"""启动app,初始化设置"""pass@given('open the main board')def open_the_main_board() -> None:"""打开登陆面板"""pass@when("I admit agreements on login board")def admit_agreements_on_login_board() -> None:"""同意用户协议"""pass@when("I go to the mail login board")def to_mail_login_board() -> None:"""到邮箱账号登录面板"""pass@when("I input correct password and login")def input_correct_password_and_mail_login() -> None:"""邮箱登录,输入正确的密码"""pass@then('assert login success')def login_success() -> None:"""断言登陆成功"""pass@scenario('mail_login.feature', 'mail login with correct password')def test_mail_login_with_correct_password() -> None:"""邮箱登录测试用例"""pass

4.1.3 代码复用示例

可复用的步骤可以放到conftest.py中,比如:start app and initialize settings,open the main board,I admit agreements on login board,代码示例如下:

# conftest.pydef initialize_environment(apk_name: str) -> None:"""初始化测试环境:param apk_name:  apk包名:return: None"""pass

对于支付宝支付的测试用例,需要登陆操作,显然可以复用邮箱登录的步骤。

# alipay_test.featureFeature: alipay_testtestcases about alipay  Scenario: 支付宝支付,编辑商品信息,拉起支付窗口,验证支付是否调起    Given start app and initialize settings    And open the main board    When I admit agreements on login board    And I go to the mail login board    And I input correct password and login    Then assert login success    When I prepare the order information with CNY    And I place an order of alipay    Then alipay cashier desk will show up

由于复用了邮箱登录的步骤,上述拉起支付宝窗口的用例,只需要专注于下订单拉起支付的逻辑。

# alipay_test.py@when("I prepare the order information with CNY")def prepare_order_information() -> None:"""准备订单信息"""pass@when("I place an order of alipay")def place_an_order() -> None:"""下订单"""pass@then("alipay cashier desk will show up")def assert_alipay_cashier_desk_shows_up() -> None:"""展示收银台"""pass

4.1.4 测试数据统计

Pytest 有钩子支持二次开发,在pytest_runtest_makereport钩子内。

def pytest_runtest_makereport(item, call) -> None:"""统计测试数据"""pass

4.1.5 回传统计数据

在钩子pytest_runtest_makereport(item, call)编写数据持久化逻辑。若使用 jenkins,则直接往数据库写数据。若使用自研服务,则调用服务端接口,回传测试数据。

def pytest_sessionfinish(session, exitstatus) -> None:"""调用调度服务的接口,上传测试数据"""pass

4.1.6 发送测试报告

jinja2库支持编写邮件网页,email库支持发送邮件。

4.2 调度服务

4.2.1 调度服务设计

调度服务和 Web 前端,可以用 jenkins 代替。相应地,开发成本降低,可以快速出效果,但是展示效果要打折扣,数据处理逻辑也更依赖 pytest 钩子的二次开发。

服务端框架推荐:Flask,可以快速出成果。以创建任务为例:

@app.route("/testtask/create", methods=["POST"])def create_test_task() -> str:pass

4.2.2 远程执行命令

本地机器生成私钥和公钥

$ ssh-keygen -t rsa

把公钥存放到远程机器上

$ scp ~/.ssh/id_rsa.pub zhancc@192.168.94.111:~/.ssh/id_rsa.pub

把公钥添加到远程机器的authorized_keys

cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

远程执行命令

ssh zhancc@192.168.94.111 "/Users/<username>/Library/Android/sdk/tools/bin/avdmanager create avd -n <emulator_name> -k system-images;android-22;google_apis;arm64-v8a"

Python 调用命令

import subprocessdef execute_bash(command: str, timeout: int = 10) -> tuple:"""执行shell脚本:param command: 脚本:param timeout: 超时时间,默认为10秒:return: stdout, stderr 标准输出和错误"""sp = subprocess.Popen(command, shell=True)return sp.communicate(timeout=timeout)

4.2.3 支持异步任务

各种命令调用,用同步任务,性能将会很差,且模块耦合较高。使用 celery 异步任务将会提高性能,以调用远程命令为例:

from utils import Machinefrom celery import Celerycelery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])celery.conf.update(app.config)@celery.taskdef execute_remote_command(machine: Machine, command):"""执行远程命令"""pass

4.2.4 支持定时任务

为了支持定时触发测试任务,推荐使用APScheduler

from apscheduler.schedulers.background import BlockingSchedulerscheduler = BlockingScheduler()scheduler.add_executor('processpool')def trigger_test() -> None:"""触发测试任务"""passscheduler.add_job(ui_test, args=("iOS_SDK_Client_Test", "mainland", "qa"),trigger='cron', minute="07", hour="5", timezone=ZoneInfo("Asia/Shanghai"))scheduler.add_listener(task_listener, EVENT_JOB_ERROR)print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))try:scheduler.start()except (KeyboardInterrupt, SystemExit):pass

4.2.5 Web 后台

利用 Vue.js 快速实现 Web 后台,在此不过多介绍。

5 结语

5.1 应用拓展

  • 系统版本的兼容性测试:由于模拟器的系统版本众多,理论上可以做 Android 和 iOS 系统版本的兼容性测试。

  • 云模拟器服务:商业案例有:https://appetize.io/demo

5.2 作者的话

今年9月份找到公司法务,想把这个发成客户端自动化测试专利,法务说你这个方案与一些已有专利相似度较高。既然发不了,不如就分享给大家,共勉!

参考文档

1.The Testing Pyramid: Simplified for One and All

2.Just Say No to More End-to-End Tests

3.Android Studio - avdmanager

4.simctl Written by Mattt November 26th, 2018

5.Best iOS simulators for Windows and Mac for 2022

6.阿里 Python 自动化工具 tidevice 使用指南

7.如何在 Android 手机上进行自动化测试(上)

8.如何在 Android 手机上进行自动化测试(下)

9.如何在 iOS 手机上进行自动化测试

10.BDD (Behavior Driven Development) Framework: A Complete Tutorial

11.Behavior-Driven Development(BDD) Testing

12.ssh 密钥登录及远程执行命令 

13.Pytest Documentation

14.Flask Documentation

15.APScheduler Documentation

16.Celery - 分布式任务队列


资源分享【这份资料必须领取~】

下方这份完整的软件测试视频学习教程已经上传CSDN官方认证的二维码,朋友们如果需要可以自行免费领取 【保证100%免费】

 

在这里插入图片描述


http://www.ppmy.cn/news/683124.html

相关文章

eclipse检测不到手机或模拟器

问题&#xff1a; eclipse检测不到手机或模拟器 原因&#xff1a; 某个程序占用了adb端口&#xff0c;如豌豆荚等各类手机助手 解决方法&#xff1a; 方法1.在DDMS里面重启adb server或者任务管理器里面 kill掉adb.exe服务方法2&#xff1a;5037为adb默认端口 查看该端口情…

ios模拟器装ipa包_给iOS 模拟器“安装”app文件

前言 刚刚接触iOS的时候,我就一直很好奇,模拟器上面能不能直接安装app呢?如果可以,我们就直接在模拟器上面聊QQ和微信了。直到昨天和朋友们聊到了这个话题,没有想到还真的可以给模拟器“安装”app! 一.应用场景 先来谈谈是什么情况下,会有在模拟器上安装app的需求。 在一…

android给手机创建sd卡,Android模拟器实现手机添加文件到sd卡的方法

本文实例讲述了Android模拟器实现手机添加文件到sd卡的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; 在DDMS中直接添加文件到模拟器sd卡如果出现错误类似&#xff1a;Failed to push XXXXX.txt on emulator- : Read-only file system的错误&#xff0c;原因是你…

Android模拟器编程,Android模拟器入门[转]

要玩google 手机 G1的模拟器&#xff0c;当然需要先去google上面下载Android的SDK&#xff0c;解压出来后在SDK的根目录下有一个tools文件夹&#xff0c;里面就是模拟器和一些非常有用的工具。 双击“emulator.exe”&#xff0c;直接启动模拟器&#xff0c;简单吧。当然&#x…

limbo模拟器镜像Android,limbo模拟器win7镜像

limbo模拟器是一款类似于vmware的安卓虚拟机软件,使用这个软件可以在安卓手机上运行主流的windows操作系统,小编提供的limbo模拟器win7镜像主要适用于手机内存比较大的用户使用,加载后就可以正常运行了,不过是否支持联网小编并未做测试。 limbo win7镜像使用教程 imbo模拟器…

Android的模拟器能不能照相,android模拟器无法使用camera拍照

遇到的问题&#xff1a; 1 在模拟器里&#xff0c;无法启动camera&#xff1b; 2 成功启动camera之后&#xff0c;真正拍照的时候&#xff0c;提示“your sdcard is full"&#xff0c;我明明有一个100M的sdcard啊&#xff01; 解决办法&#xff1a; 1 模拟器默认是disable…

CMD看累了?推荐一个不错的终端模拟器

来自&#xff1a;开源最前线&#xff08;ID&#xff1a;OpenSourceTop&#xff09; 整理自&#xff1a;https://github.com/Eugeny/tabby 你喜欢哪种终端仿真程序&#xff0c;你是喜欢轻量级呢&#xff0c;还是探索全部功能和可定制化的选项&#xff1f;或者只使用发行版给的默…

java模拟器分辨率_Android模拟器分辨率介绍

Skins&#xff1a;HVGA、HVGA-L、HVGA-P、QVGA-L、QVGA-P Android 2.0版本可用的 参数如下所列&#xff1a; Skins&#xff1a;HVGA、QVGA、WVGA800、WVGA854 Android 2.1和 2.2版本可用的 参数如下所列&#xff1a; Skins&#xff1a;HVGA、QVGA、WQVGA400、WQVGA432、WVGA800…