1. 为什么我最终选了 Python + Appium 这套组合
移动端自动化测试这个领域,工具链换了一茬又一茬,早年做 Android 自动化基本绕不开 UiAutomator 和 MonkeyRunner,iOS 那边则是 Instruments 的天下。后来 Facebook 开源了 WebDriverAgent,Appium 借着 WebDriver 协议把两端统一了起来,这才让跨平台的移动端自动化真正有了一个相对体面的方案。我大概是在 Appium 1.6 那个版本开始正式把它引入到项目里的,中间踩过的坑、换过的方案、推翻重来的架构设计,算下来能写好几篇复盘。这篇文章就把 Python + Appium 这套组合从选型到落地、从环境搭建到框架设计、从元素定位到持续集成的完整链路拆开讲一遍,适合正在做移动端自动化测试、或者准备把手工回归转成自动化回归的同行参考。
先说清楚这套方案能干什么。Appium 本质上是一个基于 WebDriver 协议的自动化服务端,它在移动设备上启动一个监听端口,接收来自客户端的 HTTP 请求,然后翻译成各平台原生的自动化指令去执行。Python 作为客户端语言,通过 Appium-Python-Client 这个库跟服务端通信。这套组合最大的价值在于:一套测试代码可以同时跑 Android 和 iOS,只要把定位策略和部分平台差异处理好,业务逻辑层的用例可以高度复用。对于中小团队来说,这意味着不需要分别维护两套自动化代码,人力成本直接砍半。
那为什么是 Python 而不是 Java 或者 JavaScript?这个问题我被问过很多次。Java 生态里 Appium 的客户端库确实成熟,Maven 依赖管理也稳,但 Java 写测试用例的样板代码太多,一个简单的点击操作要写好几行,用例的可读性差。JavaScript 那边 WebdriverIO 也不错,但 Node 生态的异步处理对测试新手不太友好,Promise 链写多了容易乱。Python 的优势在于语法简洁、上手快、断言库丰富,而且跟 pytest 这个测试框架配合起来,参数化、fixture、标记分组这些功能开箱即用。对于测试团队里非计算机科班出身的同学来说,Python 的学习曲线明显更平缓。
不过我得提前说一句,Appium 不是银弹。它的稳定性受设备、系统版本、App 本身实现方式的影响非常大,尤其是混合应用和 Flutter 应用,定位元素的时候经常会出现各种玄学问题。所以这套方案更适合原生应用为主、或者 WebView 结构比较规范的场景。如果你的 App 大量使用自绘控件或者游戏引擎渲染,那 Appium 的定位能力会大打折扣,这时候可能需要考虑图像识别或者坐标点击作为兜底方案。
2. 环境搭建:从零把链路跑通
2.1 基础依赖的安装顺序与版本匹配
环境搭建这块,我见过太多人卡在版本不兼容上。Appium 的版本、Node 的版本、各平台 SDK 的版本、Python 客户端的版本,这几者之间有隐性的对应关系,装错了就是各种报错。我建议的顺序是这样的:先装 Node.js,再装 Appium 服务端,然后装 Android SDK 或者 Xcode 命令行工具,最后装 Python 客户端库。
Node.js 的版本建议用 LTS 版本,不要追最新。Appium 2.x 之后服务端架构做了大改,驱动变成了独立安装的插件,所以装完 Appium 之后还要单独装 uiautomator2 和 xcuitest 这两个驱动。具体命令是先用 npm 全局装 appium,然后执行 appium driver install uiautomator2 和 appium driver install xcuitest。这一步很多人会漏掉,结果启动会话的时候报找不到驱动的错误。
Android 这边需要装 Android SDK,重点是要有 platform-tools 和 build-tools,环境变量里把 ANDROID_HOME 配好,PATH 里加上 platform-tools 和 tools 目录。iOS 这边必须在 macOS 上,装好 Xcode 之后要执行 xcode-select --install 装命令行工具,然后 WebDriverAgent 的编译依赖 Carthage 也要装上。Python 客户端库直接 pip install Appium-Python-Client 就行,但要注意版本,2.x 和 3.x 的 API 有一些差异,我目前用的是 3.x 版本,配合 Appium 2.x 服务端。
注意:不要用 pip 装 appium 这个包,那是另一个完全不同的东西,跟 Appium 服务端没关系。客户端库的名字是 Appium-Python-Client,中间有连字符。
2.2 设备连接与调试桥的验证
环境装完之后,第一件事是确认设备能被正确识别。Android 设备用 USB 连上之后,执行 adb devices,如果列表里能看到设备序列号就说明驱动没问题。如果显示 unauthorized,需要在手机上确认 USB 调试授权弹窗。如果压根看不到设备,Windows 上通常是驱动没装好,macOS 上一般是数据线或者 USB 口的问题。
iOS 设备稍微麻烦一点,需要用 libimobiledevice 这个工具集来查看设备信息,执行 idevice_id -l 能看到设备 UDID 就说明连接正常。真机调试还需要在 Xcode 里配置好开发者证书和描述文件,否则 WebDriverAgent 编译不过去。模拟器的话直接 xcrun simctl list 看设备列表就行。
这里有个经验:Android 模拟器和真机的行为差异比想象中大。模拟器的 GPU 渲染、网络延迟、传感器数据都跟真机不一样,有些依赖硬件特性的用例在模拟器上跑没问题,到真机上就挂。所以我的建议是,日常开发调试可以用模拟器提高效率,但回归测试一定要在真机上跑一遍。
2.3 Appium 服务端的启动参数配置
Appium 服务端启动的时候可以带一堆参数,这些参数直接影响后续会话的行为。常用的有 --address 指定监听地址,--port 指定端口,--log-level 控制日志级别,--allow-insecure 放开一些被默认禁用的特性。我一般会把日志级别设成 info,调试的时候临时改成 debug,不然日志文件涨得太快。
还有一个关键参数是 --relaxed-security,这个在需要执行 adb shell 命令或者访问文件系统的时候必须加上,否则 Appium 会拒绝执行。但生产环境慎用,因为这会降低安全性。另外如果同时连了多台设备,需要用 --udid 指定具体设备,或者用 --default-capabilities 设置默认能力。
启动命令大概长这样:
appium --address 127.0.0.1 --port 4723 --log-level info --relaxed-security服务端起来之后,浏览器访问 http://127.0.0.1:4723/status 能看到一个 JSON 响应,里面 status 是 0 就说明服务正常。这个健康检查接口在 CI 环境里很有用,可以在跑用例之前先探测一下服务是否就绪。
3. 核心概念:Desired Capabilities 与元素定位
3.1 Desired Capabilities 的必填项与平台差异
Desired Capabilities 是 Appium 会话建立时最核心的配置,它告诉服务端你要操作什么设备、什么应用、用什么驱动。这个字典里的键值对决定了整个会话的行为,配错了要么会话起不来,要么行为不符合预期。
必填的几项包括 platformName(Android 或 iOS)、platformVersion(系统版本)、deviceName(设备名称)、app(应用安装包路径)或者 appPackage + appActivity(已安装应用的包名和启动 Activity)。Android 这边 appPackage 和 appActivity 是分开的,iOS 这边则是 bundleId 一个字段搞定。
除了必填项,还有几个高频使用的可选配置。noReset 控制是否在会话开始前重置应用状态,设成 true 可以保留登录态,加快用例执行速度,但要注意用例之间的数据污染问题。fullReset 则是完全卸载重装,适合需要干净环境的场景。automationName 在 Android 上默认是 UiAutomator2,iOS 上是 XCUITest,一般不用改。newCommandTimeout 设置命令超时时间,默认 60 秒,如果有些操作耗时较长需要调大。
还有一个容易被忽略的是 autoGrantPermissions,设成 true 可以自动授予应用需要的权限,省去手动点弹窗的麻烦。但这个只在 Android 6.0 以上有效,而且不是所有权限都能自动授予。
3.2 元素定位策略的选择与优先级
元素定位是自动化测试里最耗时也最容易出问题的环节。Appium 支持的定位方式有很多种,但实际项目中常用的就那么几种,而且有明确的优先级。
首选是 accessibility id,也就是 Android 的 content-desc 和 iOS 的 accessibility identifier。这种方式最稳定,因为它不依赖 UI 层级结构,只要开发在代码里设置了就能定位到。但现实是很多开发不重视这个,导致可用的 accessibility id 很少。
次选是 id,Android 上是 resource-id,iOS 上是 name。这个也相对稳定,但要注意 resource-id 可能会因为混淆或者版本更新而变化。用的时候最好做一层封装,把定位表达式集中管理,改的时候只改一个地方。
再次是 xpath,这个最灵活但也最脆弱。UI 层级稍微一变,xpath 就失效了。而且 xpath 的查找效率低,在复杂页面上耗时明显。我的建议是能用 id 就不用 xpath,非要用 xpath 的话尽量用相对路径,不要用绝对路径。
还有 class name、uiautomator 表达式、iOS predicate 等方式,这些在特定场景下有用,但通用性不如前面几种。比如 uiautomator 表达式在 Android 上做滚动查找很方便,iOS predicate 在处理复杂条件时比 xpath 简洁。
| 定位方式 | 稳定性 | 效率 | 适用场景 |
|---|---|---|---|
| accessibility id | 高 | 高 | 开发已设置无障碍标签 |
| id | 中高 | 高 | 原生控件有 resource-id |
| xpath | 低 | 低 | 兜底方案,复杂层级 |
| class name | 中 | 中 | 同类控件批量操作 |
| uiautomator | 中高 | 中 | Android 滚动、条件查找 |
| iOS predicate | 中高 | 中 | iOS 复杂条件匹配 |
3.3 显式等待与隐式等待的配合使用
等待机制是自动化稳定性的关键。Appium 继承了 Selenium 的等待体系,有隐式等待和显式等待两种。隐式等待是全局设置,driver.implicitly_wait(10) 表示查找元素时最多等 10 秒,找不到就抛异常。显式等待是针对特定条件的等待,用 WebDriverWait 配合 expected_conditions 使用。
我的经验是,隐式等待设一个较小的值,比如 5 秒,作为兜底。然后在关键操作前用显式等待,等待特定元素出现或者可点击。这样既能保证稳定性,又不会因为全局等待时间过长拖慢整体执行速度。
显式等待的条件有很多种,常用的有 presence_of_element_located(元素出现在 DOM 中)、visibility_of_element_located(元素可见)、element_to_be_clickable(元素可点击)。对于页面跳转的场景,还可以用 staleness_of 等待旧页面元素消失,或者用 url_changes 等待 URL 变化。
提示:不要用 time.sleep() 做等待,这是最不优雅也最不可靠的方式。固定睡眠时间要么不够导致偶发失败,要么太长浪费执行时间。用显式等待让代码在条件满足时立即继续,条件不满足时才等待。
4. 测试框架设计:从脚本到工程化
4.1 页面对象模型的落地实践
如果只是写几个零散的测试脚本,直接调 driver 的方法就行。但一旦用例数量上去了,不做分层设计就是灾难。页面对象模型(Page Object Model)是业界公认的最佳实践,核心思想是把页面元素定位和操作封装成类,测试用例只调用这些类的方法,不直接接触定位表达式。
具体做法是每个页面建一个类,类属性里放元素定位器,类方法里放页面操作。比如登录页面,定位器有用户名输入框、密码输入框、登录按钮,方法有输入用户名、输入密码、点击登录。测试用例里就是 login_page.input_username("test")、login_page.input_password("123456")、login_page.click_login(),读起来跟自然语言一样。
这样做的好处是,UI 改版的时候只需要改页面类里的定位器,用例代码不用动。而且页面类可以被多个用例复用,减少了重复代码。我一般还会建一个 BasePage 基类,把公共的查找元素、点击、输入等方法封装进去,子类继承之后直接用。
4.2 pytest 的 fixture 与参数化运用
pytest 是 Python 测试框架里的首选,它的 fixture 机制特别适合管理 driver 的生命周期。我一般会定义一个 driver fixture,scope 设成 function,每个用例开始前创建会话,结束后关闭会话。这样用例之间互相隔离,一个挂了不影响其他的。
如果用例数量多,每个用例都重启 App 会很慢,这时候可以把 scope 改成 class 或者 module,让一组用例共享一个会话。但要注意用例之间的状态清理,否则会出现数据污染。我的做法是在 fixture 的 teardown 阶段执行一些清理操作,比如退出登录、清空缓存。
参数化是 pytest 的另一个利器。同一个测试逻辑要跑多组数据的时候,用 @pytest.mark.parametrize 装饰器把数据传进去,框架会自动生成多个测试用例。比如登录功能要测正确密码、错误密码、空密码几种情况,参数化之后一份代码搞定,报告里还能清楚看到每组数据的执行结果。
import pytest @pytest.mark.parametrize("username, password, expected", [ ("user1", "correct_pwd", True), ("user1", "wrong_pwd", False), ("", "any_pwd", False), ]) def test_login(driver, username, password, expected): login_page = LoginPage(driver) login_page.login(username, password) assert login_page.is_login_success() == expected4.3 测试报告与失败截图机制
测试跑完没有报告等于白跑。pytest 生态里有几个报告插件,allure-pytest 是功能最全的,能生成交互式的 HTML 报告,包含用例步骤、截图、日志、耗时等信息。配置也简单,装好 allure-pytest 之后在 pytest.ini 里加上 --alluredir 参数指定输出目录,跑完用 allure serve 命令就能打开报告。
失败截图是排查问题的关键。我一般会在 conftest.py 里加一个钩子,用例失败的时候自动截图并附加到报告里。截图文件名带上用例名和时间戳,方便对应。除了截图,还可以把页面源码也 dump 下来,有时候元素定位失败是因为页面结构跟预期不一样,看源码能快速定位问题。
日志这块,Appium 服务端的日志和客户端的日志要分开管理。服务端日志在启动的时候用 --log 参数指定文件路径,客户端日志可以用 Python 的 logging 模块记录。两边日志都带上时间戳,排查问题的时候可以对照着看。
5. 常见问题与排查技巧实录
5.1 会话建立失败的排查思路
会话建立失败是最常见的问题,报错信息通常比较模糊,需要一步步排查。首先看 Appium 服务端日志,里面会有详细的错误堆栈。常见的失败原因有几类:设备没连上、应用没安装、包名或 Activity 写错了、驱动没装、端口被占用。
设备没连上的话,Android 用 adb devices 确认,iOS 用 idevice_id -l 确认。应用没安装的话,用 adb shell pm list packages 看包名是否存在。包名和 Activity 写错的话,用 adb shell dumpsys window | grep mCurrentFocus 看当前前台应用的包名和 Activity。驱动没装的话,用 appium driver list 看已安装的驱动列表。
端口被占用的话,换个端口或者把占用端口的进程杀掉。macOS 和 Linux 上用 lsof -i :4723 查,Windows 上用 netstat -ano | findstr 4723 查。
5.2 元素定位失败的典型场景
元素定位失败的原因五花八门,我整理了几种典型场景。第一种是元素还没加载出来,这时候加显式等待就能解决。第二种是元素在 WebView 里,需要先切换 context 才能定位,用 driver.contexts 看可用的上下文,用 driver.switch_to.context() 切换。第三种是元素被遮挡,比如被弹窗或者键盘挡住,需要先关掉弹窗或者收起键盘。
第四种是元素在滚动区域外,需要先滚动到可见区域。Android 上可以用 uiautomator 的滚动查找,iOS 上可以用 mobile: scroll 命令。第五种是元素定位表达式本身有问题,比如 xpath 写错了,或者 id 变了。这种情况用 Appium Inspector 这个工具来辅助定位,它能实时显示页面结构和元素属性。
还有一种比较隐蔽的情况是,同一个页面上有多个相同 id 的元素,定位到了错误的那个。这时候需要用 find_elements 拿到列表,然后根据索引或者文本内容筛选。
5.3 用例执行不稳定的优化手段
用例偶发失败是自动化测试的通病,优化手段有几个方向。首先是等待策略的优化,把固定睡眠改成显式等待,让代码在条件满足时立即继续。其次是重试机制,对于网络请求或者动画相关的操作,失败后自动重试几次。pytest 有 pytest-rerunfailures 插件可以实现这个功能。
再次是环境隔离,每个用例跑之前把应用状态重置到已知状态,避免前一个用例的残留数据影响后一个。还有是设备性能的影响,低端设备上操作响应慢,等待时间要相应调大。最后是并发执行的问题,多设备并行跑用例的时候,资源竞争可能导致失败,需要做好设备分配和端口管理。
| 问题现象 | 可能原因 | 排查方法 | 解决手段 |
|---|---|---|---|
| 会话建立超时 | 设备未连接/驱动缺失 | 查 adb devices 和 driver list | 重连设备/安装驱动 |
| 元素找不到 | 未加载/在 WebView/被遮挡 | 用 Inspector 看页面结构 | 加等待/切 context/关弹窗 |
| 点击无效 | 元素不可点击/坐标偏移 | 查元素 clickable 属性 | 用坐标点击兜底 |
| 用例偶发失败 | 等待不足/环境干扰 | 看失败截图和日志 | 加重试/隔离环境 |
| 执行速度慢 | 隐式等待过长/频繁重启 | 看各步骤耗时 | 调小等待/复用会话 |
5.4 跨平台用例复用的取舍
一套代码跑两端听起来很美,但实际操作中要做不少取舍。Android 和 iOS 的 UI 结构差异很大,定位表达式基本没法通用。我的做法是把定位器按平台分开管理,用配置文件或者字典来区分,用例逻辑层尽量抽象成平台无关的操作。
比如点击登录按钮这个操作,Android 上可能是 find_element(By.ID, "com.example:id/login_btn"),iOS 上是 find_element(By.NAME, "loginButton")。在页面类里根据当前平台选择对应的定位器,用例层调用的还是同一个方法。这样用例逻辑只写一遍,平台差异被封装在页面类里。
但有些差异是封装不掉的,比如 Android 的返回键和 iOS 的右滑返回,操作方式完全不同。这种就需要在用例层做平台判断,或者干脆写两套用例。我的原则是,能复用的尽量复用,复用成本太高的就分开写,不要为了追求复用率把代码搞得过于复杂。
6. 持续集成与设备农场实践
6.1 Jenkins 流水线的配置要点
把自动化测试接入 CI 是发挥其价值的必经之路。Jenkins 是最常用的 CI 工具,配置流水线的时候有几个要点。首先是环境准备阶段,要确保 Appium 服务端已经启动,设备已经连接。可以在流水线里加一个健康检查步骤,探测 Appium 的 status 接口,不通就重试或者报错。
其次是测试执行阶段,用 pytest 命令跑用例,加上 --alluredir 参数生成报告。执行过程中要捕获退出码,非零就标记构建失败。然后是报告生成阶段,用 allure 命令把结果转成 HTML 报告,归档到构建产物里。最后是清理阶段,关闭 Appium 会话,断开设备连接,清理临时文件。
Jenkins 的 agent 配置也很关键。如果设备连在物理机上,需要把任务调度到那台机器上执行。可以用标签来标记有设备的节点,流水线里指定 agent 标签。多设备并行的话,可以用 Jenkins 的并行阶段功能,每个设备跑一个子任务。
6.2 多设备并行执行的调度策略
多设备并行能大幅缩短测试时间,但调度不当会引入新的问题。首先是设备分配,要确保每个设备同时只跑一个任务,否则会出现操作冲突。可以用一个设备池来管理,任务开始前从池里取设备,结束后归还。
其次是端口管理,每个 Appium 服务端实例要监听不同的端口,客户端连接的时候指定对应端口。我一般用设备序列号的哈希值来生成端口号,避免冲突。还有是系统资源的限制,一台机器上同时跑太多设备会导致 CPU 和内存吃紧,影响执行稳定性。一般一台机器跑 4 到 6 台设备比较合适,具体看机器配置。
并行执行还会带来日志和报告的混乱问题。每个设备的日志要分开存,报告里要能区分是哪个设备跑的。Allure 报告支持按环境或者标签分组,可以在用例里加上设备标识的标签,报告里就能按设备筛选。
6.3 测试数据的准备与清理
测试数据的管理是自动化测试里容易被忽视但很重要的一环。用例执行前需要准备数据,执行后需要清理数据,否则数据会越积越多,影响后续执行。我的做法是用 fixture 来管理数据生命周期,setup 阶段创建数据,teardown 阶段删除数据。
数据创建可以通过 API 调用来做,比 UI 操作快得多也稳定得多。比如创建一个测试用户,直接调后端的注册接口,不用走 UI 流程。数据清理也是同理,调删除接口把测试数据清掉。如果后端没有提供 API,那就只能走数据库操作,用 Python 的数据库驱动直接执行 SQL。
还有一种情况是数据无法删除,比如订单数据涉及业务逻辑不能随便删。这时候可以用标记的方式,给测试数据打上特殊标记,查询的时候过滤掉。或者用独立的测试环境,数据跟生产隔离,定期重置环境。
7. 我踩过的那些坑与经验总结
7.1 输入法导致的输入失败问题
这个坑我踩过不止一次。Android 上输入中文的时候,如果设备默认输入法是搜狗或者讯飞,Appium 的 send_keys 经常会失败或者输入乱码。原因是这些第三方输入法有自己的候选词逻辑,跟 Appium 的输入机制冲突。解决办法是把设备输入法切成系统自带的,或者用 adb shell ime 命令临时切换。
更彻底的办法是用 mobile: type 这个扩展命令,它直接往输入框里塞文本,不经过输入法。但有些应用对输入方式有校验,这种方式可能不生效。还有一种是用 adb shell input text 命令,但同样有中文支持的问题。我的经验是,能用英文测试数据就用英文,非要用中文的话,提前把输入法切好。
7.2 弹窗与权限对话框的处理
应用启动时的权限弹窗、更新提示、广告弹窗,这些是自动化测试的天敌。处理方式有几种:一是用 autoGrantPermissions 自动授权,但只对部分权限有效。二是用 Appium 的 autoAcceptAlerts 能力自动接受弹窗,但 iOS 和 Android 的行为不一样。三是写一个通用的弹窗处理函数,在每次操作前检查有没有弹窗,有就关掉。
我一般会在 BasePage 里加一个 handle_alert 方法,用 find_elements 查找弹窗元素,存在就点击关闭按钮。这个方法在每个页面操作前调用一次,虽然有点冗余,但能避免大部分弹窗导致的失败。对于系统权限弹窗,还可以用 adb shell pm grant 命令提前授予权限,从根源上避免弹窗出现。
7.3 混合应用与 WebView 的定位难题
混合应用里嵌了 WebView,WebView 里的元素用原生定位方式是找不到的,必须先切换到 WebView 的 context。切换之前要确保 WebView 已经加载完成,可以用 driver.wait_for_webview() 或者轮询 contexts 列表。
切换到 WebView 之后,定位方式就变成跟 Selenium 一样了,用 CSS 选择器或者 xpath。但要注意,WebView 里的元素定位效率比原生低,而且受页面加载速度影响大。我的做法是在 WebView 操作前加一个显式等待,等关键元素出现再操作。操作完切回原生 context,避免影响后续步骤。
还有一个坑是,有些应用的 WebView 调试开关没打开,导致 Appium 看不到 WebView 的 context。这种情况需要开发在代码里打开 WebView 的调试模式,或者用 Chrome 的远程调试工具来辅助定位。
7.4 设备农场的维护成本
设备农场听起来很美好,实际维护起来成本不低。设备会老化、系统会升级、数据线会坏、USB 口会松动,这些都是日常要处理的问题。我建议至少准备一台备用设备,主力设备出问题的时候能顶上。
设备还要定期重启,长时间运行会出现内存泄漏、响应变慢的问题。我一般设置一个定时任务,每天凌晨重启所有设备。另外设备的系统版本要统一,不然用例在不同版本上行为不一致,排查起来很麻烦。如果必须测多个版本,那就按版本分组管理,每组用独立的设备。
充电也是个大问题。设备一直插着 USB 会过充,电池鼓包是常见故障。可以用带开关的 USB Hub,定时断电充电。或者用无线充电板,但要注意发热问题。这些细节看起来不起眼,但直接影响设备农场的可用性。
8. 从自动化到智能化的演进方向
自动化测试跑通之后,下一步可以考虑一些进阶方向。比如用图像识别来兜底那些定位不到的元素,OpenCV 做模板匹配,或者用 OCR 识别文本。再比如用机器学习来做用例的智能推荐,根据代码变更自动选择需要回归的用例,减少全量回归的时间。
还有一个方向是跟研发流程深度集成,代码提交触发自动化测试,测试结果直接反馈到代码评审里。这样开发在提交代码的时候就能知道有没有破坏现有功能,问题发现得越早修复成本越低。这需要 CI 工具和代码托管平台做集成,技术上不难,难的是推动团队接受这种流程。
设备农场也可以往云化方向发展,把设备集中管理,通过 Web 界面远程访问和调度。这样测试人员不用守在设备旁边,在家也能跑用例。但云化之后网络延迟和设备共享的问题需要解决,适合团队规模较大、设备数量较多的场景。
我个人觉得,自动化测试的价值不在于替代手工测试,而在于把重复性的回归工作交给机器,让人去做更有创造性的探索性测试。工具是死的,人是活的,把工具用好、用对场景,才能真正提升测试效率和质量。这套 Python + Appium 的方案我用了几年,中间换过框架、重构过代码、踩过无数坑,但整体来说它是目前移动端自动化测试里比较务实的选择。如果你正准备入这个坑,希望这篇东西能帮你少走点弯路。