ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Appium移动端自动化测试:从环境搭建到实战全解析

Appium移动端自动化测试:从环境搭建到实战全解析 1. 为什么移动应用自动化测试绕不开 Appium前后端分离、快速迭代、多端适配这些词在移动互联网项目里几乎天天能听到。作为测试工程师如果还停留在手工点屏幕、反复回归主流程项目节奏稍微一起来很快就成了瓶颈。我自己这几年在移动端自动化上踩了不少坑最后一直留着 Appium 这个工具没放原因很简单它能同时把 Android 和 iOS 捏到同一套技术栈里而且社区的沉淀足够厚遇到问题基本都能搜到解法。这篇文章就用一个真实项目从零到一的完成过程把 Appium 这套东西给你讲透包括环境搭建、元素定位、代码封装、调度执行以及我在实际工作中踩过的各种奇奇怪怪的坑。Appium 能做什么简单说它是一个基于 HTTP 协议的服务端程序把移动端的原生控件操作转换成一套统一的标准接口你用 Python、Java、JavaScript 这类熟悉的语言写测试脚本脚本通过 Appium Server 转成对 Android 或 iOS 设备的真实操作指令最后再由脚本去断言界面和业务逻辑是否符合预期。比如自动打开一个 App、点击登录按钮、输入账号密码、滑动列表、截屏比对这些手工重复度极高的动作都可以通过 Appium 脚本稳定地跑起来还能接着接入 CI持续集成平台在每次代码提交通道里自动完成回归验证。什么人必须认真学这个做了两三年移动端测试、想往高级测试开发或者自动化测试架构方向走的人尤其适合。我见过不少同学手工测试功底很强业务也很熟但一到自动化就觉得“不知道怎么下手”其实就是缺一套完整的最小可运行骨架。另外移动应用开发者也适合了解一下这套玩法至少你在自测功能的时候可以用脚本把一些主要链路跑一遍不用每次都手动点来点去。Appium 的学习曲线不算陡但坑是真的多就好比做菜食谱翻了几遍觉得都懂真上灶才发现火候、锅具、油温全是变量。这篇文章我尽量把变量都提前摆出来。文章里的代码和配置没有特别指定某台设备只基于常见实践大家照着自己的设备环境做对应调整就能跑起来。2. 移动端自动化测试的整体设计与技术选型2.1 Appium 的核心架构与工作原理在真正动手装环境之前必须先搞明白 Appium 到底是怎么工作的。它的整体架构很像一层“中转站”测试脚本运行在你的电脑上使用 Appium 提供的 Client 库发起 HTTP 请求Appium Server 收到请求后根据不同移动端平台调用对应的驱动组件最终把指令给到设备上的自动化引擎。Android 端走的是 UIAutomator2 或 Espresso 驱动iOS 端走 XCUITest 驱动而驱动在底层是通过系统对外开放的测试接口来操作控件树的。这个设计带来的直接好处是跨平台统一。你写脚本的时候不用关心设备内部到底用的什么技术去驱动只需要面对 Appium 抽象出来的 API比如 click、send_keys、 find_element、swipe 这些方法。从这个角度看Appium 其实就把“操控移动设备”这件事变成了一套标准接口跟 Web 自动化里的 Selenium 干的事非常像只不过 API 更偏向移动端的触摸、滑动、多手势这些场景。再说说 Appium 的两个重要版本一个是老牌的 Appium 1.x一个是 Appium 2.x。Appium 2.x 从设计上做了大调整驱动本身拆掉了内置的移动端组件改成按需安装的插件机制比如需要跑 Android 就安装 uiautomator2 驱动需要跑 iOS 就安装 xcuitest 驱动。刚开始上手的话我建议直接用 Appium 2.x因为你装一套环境只管自己需要的平台比 1.x 省心不少也顺带避开了一些旧版本的兼容性问题。官方还在持续更新 2.x社区里新出的问题和修复也是优先落在 2.x 上。2.2 为什么选择 Appium 而不是其他自动化方案移动端自动化测试可选方案并不少。常见的有三大类第一类是 Appium 这种跨平台黑盒方案脚本与 App 源码无关对着安装包和真实的界面做操作第二类是 Android 平台独有的 UiAutomator 原生测试框架还有 iOS 平台自带的 XCTest它们通常需要和源码工程集成在开发自测阶段用得多第三类是一些商业工具或者云测平台但往往需要付费脚本语言和自由度也有限。对比下来Appium 的优势在于三点。一是技术栈自由你可以用已经熟悉的 Python、Java、JavaScript 写测试脚本不需要为了测试专门去学一套陌生的语言二是 Appium Inspector 这类辅助工具成熟可以非常直观地查看界面元素树、属性、坐标对新手尤其友好三是社区生态庞大几乎所有网上能搜到的移动端自动化问题都有人用 Appium 踩过并给出答案。当然Appium 也不是没有代价它走的是中间层转发执行速度会比原生测试框架慢一些如果你要跑上千条用例对设备和执行机的性能要求会更高。这在绝大多数业务回归场景里是可以接受的但如果你的项目对执行速度有极致要求那就需要评估一下是否值得为了性能去绑到特定平台上。在团队落地的时候我见过的比较稳的组合是Appium 负责端到端主干流程验证加 pytest 管理用例和断言再套上 Allure 做测试报告最后用 Jenkins 定时拉起来跑回归。如果你的项目是 Java 体系套 TestNG 或其他测试框架也一样思路是通用的。这就引出了下一个问题如何搭建一个既能跑通又能维护的自动化测试基础工程。2.3 一个可落地的自动化测试工程应该长什么样很多新手学 Appium 卡在“只会在单个脚本里点击”的状态就是因为他没有建立“工程化”的概念。自动化测试不是把操作步骤写进一个 .py 文件然后 run而是要把设备连接、App 启动、元素定位、业务操作、数据断言、报告输出、异常恢复这几块逻辑拆开。我习惯把工程分四层第一层是公共配置层专门放设备参数、App 路径、测试环境地址等全局信息第二层是基础封装层把启动 App、等待元素、点击、输入、滑动这些动作封成可复用的方法第三层是页面对象层按“一页面对应一个类”的方式把某个界面的元素和交互方法集中起来第四层是测试用例层只写业务步骤和期望结果。页面对象模式也就是常说的 Page Object我强烈建议早点养成习惯别觉得“我这个项目就几个用例直接写在脚本里省事”。真实项目里界面改动的频率比你预想的高如果元素定位散落在各个用例里一处数据属性变了你得把所有脚本都翻出来改而如果每个页面有专门的类只需要改一个地方。这跟代码里把重复逻辑抽成公共函数是一个道理前者是给测试代码做“结构化复用”。好架构层说透了我们进入实战环节。接下来要装的东西比较多但每一件都有明确用处我会告诉你为什么一定要装怎么验证装成功了而不是只甩给你一串安装命令就完事。3. 从零搭建 Appium 环境工具链逐项拆解3.1 准备 JDK 与 Android SDKAppium 要操作 Android 设备底层是通过 adbAndroid Debug Bridge来通信的而 adb 又是 Android SDK 里的一个组件。所以 Android SDK 是必须的。同时 Appium 本身是 Java 写的电脑上也要有 JDK。很多同学在这里第一步就被卡住大多数原因是没有搞清楚 JDK 版本和 Android SDK 版本之间的配合关系。我自己在 Windows 上测过一条比较省心的路径先去官网装 JDK 17然后用 IDE 或命令行跑一下java -version确认安装成功。Android SDK 的安装我推荐用 Android Studio 来装因为 Android Studio 的 SDK Manager 图形界面很直观勾选几个必要组件就能完成省去手动配环境变量的麻烦。装 Android Studio 倒不是为了写 App纯粹是为了拿它自带的 SDK。SDK 装好后记住安装路径后面配置环境变量的时候要用。接下来配置 ANDROID_HOME 环境变量这是 Appium 查找 Android 相关组件的关键变量。然后在 PATH 里加上platform-tools目录这样命令行里就能直接用 adb 命令了。配置完成后打开一个新的命令行窗口分别执行adb version和appium-doctor如果没有 appium-doctor 就先装一个下面会提到做一次体检。如果 adb 版本号正常打印出来说明环境变量生效了。这里有个小建议尽量别用 Android Studio 自带的那个 jre单独安装 JDK 在路径上更可控后面排查问题的时候也更省事。命令行窗口在修改环境变量之后一定要重新开一个因为新开的窗口才会加载最新的环境变量设置。3.2 安装 Appium Server 与 Appium InspectorAppium 的最新版本通常通过 Node.js 的 npm 包管理器来安装。如果你电脑上有 Node.js 环境直接执行npm install -g appium就能把 Appium Server 装好。安装完成后命令行执行appium --version确认版本。接着还需要为 Android 平台安装驱动Appium 2.x 的最新驱动安装命令是appium driver install uiautomator2如果你要测 iOS就安装xcuitest驱动。这些驱动各自管理各自平台的协议转换按需安装的好处是减少环境体积也避免版本冲突。有了 Server 还需要一个能和设备“对话”的可视化工具就是 Appium Inspector。它主要干两件事第一连接设备后展示屏幕截图和界面控件树帮你查看每个元素的 id、xpath、class、accessibility id 等定位信息第二可以在工具里直接试验点击、查找、滑动操作快速验证你得出的定位表达式到底能不能定位到元素。我强烈建议新手把元素定位的调试都放在 Inspector 上完成不要在脚本里一次一次试错那样效率太低。Appium Inspector 的获取方式有两种一种是通过 GitHub 官方 Release 下载桌面版另一种是直接在命令行里通过 npm 安装后再启动。下载桌面版的好处是可以独立运行不占命令行资源。有一点容易踩坑Inspector 连接设备时同样需要配置 Desired Capabilities设备描述参数而且它默认连接的端口是 4723所以启动 Appium Server 时最好就用默认端口避免不必要的烦恼。3.3 配置 pytest 与 Python 开发环境测试脚本这一侧我选 Python pytest原因前面已经提过上手快、写法简单、断言体系成熟。需要安装的第三方库主要有三个Appium-Python-Client提供 Appium 的 Python 客户端方法pytest负责用例收集、执行与断言pytest-html或allure-pytest用来生成可视化的测试报告。安装命令都很直接用pip install Appium-Python-Client pytest pytest-html就能搞定。如果你想用 Allure 报告还需要额外下载 Allure 命令行工具并且配置到 PATH 里。这里有个小提醒Appium-Python-Client的版本最好和 Appium Server 的大版本匹配装完可以通过pip show Appium-Python-Client查看版本号如果 Server 是 2.xClient 也尽量用 2.x 以上因为旧版 Client 很多接口签名在老版本上会变化照着网上的教程写会报奇奇怪怪的错误。还需要在电脑上准备一个真正的 Android 设备或者在 Android Studio 里启动一个模拟器。模拟器启动之后通过adb devices检查设备状态看到设备编号加上device状态说明这个设备已经被识别可以成为自动化操作的对象了。永远不要忽略 adb devices 这一步后面大量问题的排查起点都在这里。3.4 用 appium-doctor 检查环境完整性在我教过的很多新人那里环境没装干净是出现最多的问题。Appium 官方很贴心提供了一个环境自检工具叫appium-doctor安装方式就是npm install -g appium-doctor。装完以后在命令行执行appium-doctor --android它会检查 JDK、Android SDK、ANDROID_HOME、adb 等多个依赖项每一项后面会有绿色打勾或者红色打叉的标记打叉项如果显示了缺什么照着补即可。有的同学装完了发现 appium-doctor 提示找不到 Java但是自己明明装了 JDK。这种情况绝大多数是因为安装 JDK 后没有配置JAVA_HOME环境变量。appium-doctor不是智能到去扫描你电脑上所有 Java 安装位置的它只认环境变量指向的路径。所以建议配置完所有环境变量之后再回头跑一遍 appium-doctor确认每一项都有效再继续往下走。排查环境问题的时候别靠直觉先跑这个工具它能筛掉一大半低级的配置错误。4. 第一次运行 Appium 自动化设备连接与核心配置4.1 理解 Desired Capabilities 的作用Desired Capabilities 是 Appium 会话开始时的一组 JSON 键值对用来告诉 Appium Server要测什么平台、平台的版本是多少、要启动哪个 App、用哪种驱动等等。这组参数很像包裹的“寄件单”Server 根据它找到合适的设备、合适的驱动、合适的启动方式。我第一次学 Appium 时忽略了这个概念以为 setup 只是走个过场结果后面所有“会话无法建立”的问题都跟它脱不开干系。以 Android 为例有几个必填项platformName必须写成Androidappium:automationName指定为UiAutomator2platformVersion是手机系统的 Android 版本号deviceName可以是设备型号或 adb devices 里显示的标识符app指向待测 App 的安装包路径。如果你要测的是已经安装在设备上的 App就不传app而是传appPackage和appActivity说明你要启动哪个应用入口。传 apk 和传已装 App 的包名是两种不同模式前者适合首次安装后测试后者适合持续回归时省去重装 App 的时间。另外还有几个可选但很实用的参数appium:noReset设置为 true 表示不重置 App 的本地数据这样登录状态和缓存可以保留appium:unicodeKeyboard和appium:resetKeyboard通常配合使用解决中文输入框无法键入文字的问题。真实项目里这些参数会和项目环境、测试策略强相关没有一套绝对固定的模板你要理解每个参数干了什么再按需调整。4.2 从 adb 连接检查到启动 Appium 会话开始跑脚本之前设备侧要先通过 adb 打通连接。如果是真机用数据线连上电脑后在手机里打开开发者选项开启 USB 调试部分手机还需要在弹出授权框时点允许。连接后在命令行执行adb devices你应该看到类似emulator-5554 device或一个真实的设备序列号。如果状态是unauthorized通常是因为手机上没允许调试授权如果状态是offline多半是数据线不支持数据传输或者驱动有问题换一根线或插拔一次试试。接着启动 Appium Server。在命令行直接输入appium它会默认监听4723端口。终端里出现 “Appium REST http interface listener started on 0.0.0.0:4723” 这种日志就代表 Server 已经就绪。保持这个窗口开着另开一个新窗口去运行你的 Python 脚本。下面我用一段最简代码演示一次性创建会话、执行一个点击、再关闭会话的全过程。注意这里的启动参数我只写了最常见的必填项实际操作时你要按自己的设备和 App 情况补充from appium import webdriver caps { platformName: Android, appium:automationName: UiAutomator2, appium:deviceName: emulator-5554, appium:platformVersion: 12, appium:appPackage: com.example.app, appium:appActivity: .MainActivity, appium:noReset: True, } driver webdriver.Remote(http://127.0.0.1:4723, caps) # 找到页面上的登录按钮并点击 login_button driver.find_element( byAppiumBy.ID, valuecom.example.app:id/btn_login ) login_button.click() driver.quit()这段代码跑通你的第一个 Appium 会话就算成功了。这里有几个容易犯错的地方platformVersion填错会导致会话失败虽然它看起来就是个版本号appPackage和appActivity必须和实际安装包一致你可以用adb shell dumpsys window | grep mCurrentFocus之类的命令查当前前台 App 的包名和界面名。如果第一次启动 App 的时候设置了取消动画、更改权限这些操作画面加载会有延迟代码里最好加上等待逻辑这一点后面细讲。4.3 借助 Appium Inspector 完成元素定位调试在自动化测试里元素定位是日常维护中最耗时的一环。Appium Inspector 就是帮你把界面上所有控件的属性摊开来你就能找到可靠稳定的定位依据。打开 Inspector填好 Appium Server 地址 4723再填上和脚本里一致的 Capabilities点启动会话工具会连接设备并自动截取当前屏幕。左侧显示屏幕截图右侧显示控件树你点击左侧某个控件右侧会给出它的详细属性包括 text、resource-id、class、content-desc、bounds 这些。定位一个元素从稳定的属性入手优先级通常是这样content-desc对应 accessibility id最高因为它通常是产品为无障碍功能设计的比较固定其次是 resource-id一般也不会频繁变化再往后是 xpath可以用控件层级关系来定位最后是坐标点坐标最不稳定设备分辨率一变就要全改。Appium Inspector 的 Search 功能可以直接测试你写好的表达式比如通过 resource-id 找元素选中后点击 “Tap” 按钮设备上会真实触发一次点击这是验证定位是否有效的快捷方式。很多新手会陷入两个误区。一个是什么都想着用 xpathxpath 表达式写得又长又脆弱界面上一个节点顺序变化就挂了另一个是过于依赖 text 文本文本内容在国内的 App 里经常随着运营策略变化用文字定位一旦文案改了用例就翻了。我个人的习惯是能用 content-desc 不用 resource-id能用 resource-id 不用 xpath坐标除非没有其他选项否则不用。5. 编写可维护的测试脚本从元素定位到业务用例5.1 显式等待与隐式等待到底怎么选移动端界面加载受网络和渲染影响很大上一秒元素还不存在下一秒才出来。此时如果脚本直接去查找会抛NoSuchElementException。处理这类问题的标准方案是“等待元素就绪”但很多项目跑挂了就是等待方式没用对。Appium 客户端内置了三类等待方式。第一种叫隐式等待通过driver.implicitly_wait(10)设置一个全局超时时间在超时时间内反复尝试查找元素第二种叫显式等待通过WebDriverWait配合条件函数精确控制等待某个元素出现、可点击或消失第三种是强制等待也就是time.sleep()虽然简单粗暴但会让用例白等固定时间网络快的时候也浪费。我自己的经验是隐式等待适合做一个兜底比如设成 5 到 10 秒但别把它当万能药因为它对元素查找的每次重试都会有影响。显式等待是自动化用例里的主力尤其是遇到登录、刷新这种耗时操作等待“下一个页面标志性元素出现”比固定睡几秒更精准。我来举个例子假设点击登录后要等首页的“我的”标签出现再继续操作from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from appium.webdriver.common.appiumby import AppiumBy wait WebDriverWait(driver, 10) profile_tab wait.until( EC.presence_of_element_located( (AppiumBy.ID, com.example.app:id/tab_profile) ) )这段话的意思就是10 秒内每隔一小段时间就去寻找tab_profile这个元素找到了立刻继续超时自动报错。用这种写法用例跑得快的时候不用等满 10 秒跑得慢了也能容错测试时间的弹性一下就出来了。团队里我比较反对滥用time.sleep因为它会把有效执行时间拉长也不会让用例变得更可靠。5.2 封装一个通用操作层把细节收起来项目里测试用例对应的操作比如点击、输入、滑动、查找如果每个用例都直接调 Appium 原生 API代码会很散而且定位策略一改所有用例跟着遭殃。所以我会习惯性地把基础操作封装成一个类让业务用例面向封装后的方法去编程。这样有几个很直接的好处第一你可以在统一的位置加日志每个操作执行前后能输出设备和元素信息排查问题时有迹可循第二可以在统一位置加失败截图处理用例挂了自动留存截图不再需要到处写 try-catch第三是“等待元素出现再执行操作”这类公共逻辑只在封装处写一遍所有用例自动继承。举个例子我把点击操作封装成这样class BasePage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def click_element(self, locator): element self.wait.until(EC.presence_of_element_located(locator)) element.click() print(f点击元素: {locator})这里的locator可以是元组比如(AppiumBy.ID, com.example.app:id/btn_login)。通过这个封装用例里只需要写page.click_element((AppiumBy.ID, xxx))。如果后续要调整等待策略、增加重试机制所有的用例都能自动受益。封装不是越复杂越好而是要把“测试过程中一定会重复做的事”抽出来避免“复制粘贴式开发”。5.3 pytest 组织用例与断言机制pytest 是这套流程里的执行框架负责扫描以test_开头的方法或函数收集成测试用例然后按顺序或规则执行并给出通过、失败、错误等状态。移动端自动化测试用例放在 pytest 里通常一个业务链路就是一个用例。比如“登录-进入首页-打开消息中心-断言消息列表加载完成”就是一个端到端用例。pytest 里最简单的断言就是assert比如判断某个文本是否出现在页面上def test_login_and_check_home(): driver login_flow() home_text driver.find_element(AppiumBy.ID, com.example.app:id/home_title).text assert home_text 今日推荐这样写的断言虽然简单但已经能解决大部分“页面有没有展示正确内容”的验证。如果是要验证列表数据条数、接口返回字段和界面一致性可以在脚本里调用 App 的接口或者查数据库不过这就超出了基础入门范围这里不深入展开。pytest 还有一个非常有用的功能叫 fixture它可以承担“所有用例执行前的公共初始化”和“所有用例结束后的清理工作”。比如每个用例执行前都要连接设备、创建会话结束后要关闭会话就可以一次性写在 fixture 里。这样用例本身更干净只保留业务操作和断言。我习惯把 fixture 放在conftest.py文件里pytest 会自动发现如上配置。案例代码我贴一个小型框架样例方便照着起步import pytest from appium import webdriver pytest.fixture(scopemodule) def driver(): caps { platformName: Android, appium:automationName: UiAutomator2, appium:deviceName: emulator-5554, appium:appPackage: com.example.app, appium:appActivity: .MainActivity, appium:noReset: True, } driver webdriver.Remote(http://127.0.0.1:4723, caps) yield driver driver.quit()写完 fixture在用例函数里只要加一个参数driver框架会自动把创建好的会话实例传进去。通过这种方式你可以在一个文件里写多个不同业务的测试用例而不用每个用例从头启动一次 App。5.4 测试报告输出与失败截图测试结果不能只停留在命令行输出上团队协作和问题定位都需要一份像样的报告。pytest 配合插件很容易生成 HTML 报告在命令行执行pytest --htmlreport.html跑完后会在当前目录生成一个包含用例名、执行时间、状态、失败日志的网页文件。如果你想在报告里看到每个步骤的截图可以自己封装一个 pytest 钩子在用例失败时调用 driver 的截图方法保存图片并把图片路径追加到报告里。这个改造不复杂但对团队的效率提升是直接的否则测试挂了你还得重新跑一遍手工流程去复现。Allure 是我更常用的一套方案输出很美观支持步骤层级、附件、历史趋势适合做长期维护的测试集。接入方式稍微复杂一点要装 allure-pytest 插件和 Allure 命令行工具执行时参数也略有区别。不过我建议先把 pytestHTML 跑顺等用例量上到几百条时再考虑上 Allure会更符合实际成长曲线。6. 一个从启动到断言的完整案例拆解很多文章喜欢把 Appium 的例子里只放“找到按钮并点击”。但真正的业务实践往往是一个连续场景启动 App、跳过多语言弹窗、登录、进入首页、滑动列表、进入详情、断言结果。我把这些串起来写一个比较贴近真实项目的端到端用例。为了减少无关干扰这里用一个购物类 App 的简化场景来演示你完全可以照着这个思路替换成自己的业务。我先定义页面对象把“登录页”封装成一个类from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class LoginPage: def __init__(self, driver): self.driver driver self.wait WebDriverWait(driver, 10) def input_username(self, text): el self.wait.until(EC.presence_of_element_located( (AppiumBy.ID, com.example.app:id/username))) el.send_keys(text) def input_password(self, text): el self.wait.until(EC.presence_of_element_located( (AppiumBy.ID, com.example.app:id/password))) el.send_keys(text) def tap_login_button(self): el self.wait.until(EC.element_to_be_clickable( (AppiumBy.ID, com.example.app:id/btn_login))) el.click()然后写用例def test_login_flow(driver): login_page LoginPage(driver) login_page.input_username(testuser) login_page.input_password(123456) # 收起键盘避免键盘遮挡登录按钮 driver.hide_keyboard() login_page.tap_login_button() # 登录后进入首页断言首页标志元素出现 home_title WebDriverWait(driver, 15).until( EC.presence_of_element_located( (AppiumBy.ID, com.example.app:id/home_title) ) ) assert home_title.text 首页这个用例看起来很短但它已经覆盖了测试的核心输入账号密码、收起键盘、点击登录、等待页面跳转、断言首页状态。你在跑这个脚本时需要关注几个节点的表现。第一在输入账号密码时如果软键盘弹出后挡住了登录按钮脚本点击会失效或者点到了错误的位置所以要用 hide_keyboard 收起键盘。第二如果 App 登录后有一个网络请求过程等待时间设得太短会误报失败这里设了 15 秒如果网络慢可以再调大。第三也是很多人容易漏掉的一点断言不能只判断页面不崩要判断“用户关心的价值是否达成”比如首页标题文案是否正确。实际操作时你还可以把这类用例继续扩展。比如断言登录失败时出现错误提示文案断言退出登录时回到登录页断言登录态在杀掉 App 后依然保留。业务逻辑里的每一种路径都可以拆解成一组这样的端到端用例。把这些用例集合到一个目录下用 pytest 批量执行一份基础版的移动端自动化测试集就成型了。7. 常见问题与排查技巧实录7.1 会话建立失败错误日志看不懂状态执行脚本时报WebDriverException或者SessionNotCreatedException日志甩了一大串很容易吓到新手。其实解决办法很简单第一眼看错误描述里的关键字段比如An unknown server-side error occurred while processing the command.下面的Original error:部分那里通常才是真正的原因。我见过最多的情况有四种。第一种是 Appium Server 没启动干净或者端口被占用了可以关掉所有 Node 进程后重新启动 Appium。第二种是appPackage或appActivity写错了App 进不了正确的页面就建立不了会话你可以用adb shell里的包管理命令去核实应用信息。第三种是驱动没装Appium 2.x 如果没装 uiautomator2 驱动会直接报 “Please install the corresponding driver”按提示补装即可。第四种是设备连接异常会话建立前就发现deviceName与 adb 实际设备不匹配建议检查 adb devices 输出。日志阅读上别从上往下一整页硬看先搜索 “Original error”从它开始往后看抓实质原因。7.2 元素定位不到控件明明在屏幕上这个问题的排名能进 Appium 问题排行榜前三。元素在屏幕上显示但脚本找它时被NoSuchElementException打断原因可能是它还在加载中也可能是它的属性和你以为的不一样。先排除加载问题用显式等待不要急着瞬间查找。再排除属性问题打开 Appium Inspector 再看一眼它的 resource-id、content-desc、class是不是和代码里的完全一致。还有一类特殊场景元素在层级里是存在的但处于不可见状态或者上面遮了个弹窗导致页面上看着有控件却点不了。这种情况建议先检查上方有没有权限弹窗、升级弹窗、活动浮标。在跑自动化之前也可以用 Capabilities 里的autoGrantPermissions参数来自动授予 App 所需权限避免弹窗干扰。如果 UI 控件是 WebView 渲染的普通控件查找方式是找不到内部 HTML 节点需要切换到 WebView 上下文再定位这个就不在 Android 原生控件范围内了属于进阶话题但知道有这回事能帮你判断问题方向。7.3 中文输入不进文本框真机上用send_keys输入中文不少同学遇到过内容上屏出错或者没输进去的情况。这个问题的根源通常不是 App 端而是 Appium 使用的输入法不够完善。解决办法就是前面提过的两个 Capabilities 参数caps[appium:unicodeKeyboard] True caps[appium:resetKeyboard] TrueunicodeKeyboard启用 Appium 内置的 Unicode 输入法resetKeyboard在会话结束后把输入法恢复成原来的设置。两个参数建议成对出现。如果你用的是模拟器输入法问题相对少一些真机上如果还是不行可以在代码里先点击输入框再直接粘贴文本到剪贴板用driver.set_clipboard_text和长按粘贴的方式绕过去。这种做法虽然偏“技巧流”但解决不了的时候很管用。7.4 用例跑一长串后失败了怎么定位是哪一步出问题移动端测试跑批量和单个用例的感觉很不一样单个跑得好好的串起来跑就会出现各种依赖问题。最常见的是用例间状态残留比如上一个用例没有退出登录下一个用例却假设从登录页开始。解决方法是依赖 fixture 做清理比如用例结束之后执行driver.terminate_app(package)或点击退出登录。如果你发现失败用例总是和特定顺序有关优先怀疑用例间互相干扰而不是脚本本身的业务逻辑问题。排查定位还有一个非常高效的手段在关键步骤加日志和截图。我自己的习惯是在每次点击和断言前都打印当前页面标题、页面源码片段并且至少在失败时自动截图。pytest 里可以通过request.node拿当前用例名然后把截图存到以用例名命名的文件里。这样跑完一轮直接看失败用例的截图基本能判断是弹窗遮挡、文案变化还是流程跳转异常。平时看日志不要只看成功还是失败“在哪个页面、点了什么、发生了什么操作”才是最快定位的关键信息。7.5 一张速查表帮你收敛常见的异常异常现象常见原因处理建议会话建立失败端口占用、驱动缺失、包名/Activity 错误查 Original error按提示补驱动、核实包名找不到元素加载延迟、属性错误、弹窗遮挡用显式等待重新打开 Inspector 核实属性输入中文失败输入法不支持、键盘未收起启用 unicodeKeyboard/resetKeyboard点击位置偏移软键盘弹出、分辨率不同输入后收起键盘控件优先用布局定位跑批偶发失败用例间状态残留、网络超时完善 fixture 清理逻辑关键操作加重试机制设备离线USB 线问题、调试授权失效换线、重新插拔、在手机上重新授权8. 关于持续集成与后续扩展的个人体会到这里一条完整的 Appium 自动化测试链路已经从环境、脚本、定位、运行到问题排查都过了一遍。如果你把上面的代码样例照着自己项目改一改能跑通第一条用例这个东西就算入了门。可是要把它真正用在工作里产生价值我建议你马上想一件事怎么让它每天自动跑起来。我自己团队里的做法是接 Jenkins。把测试代码提交到代码仓库后在 Jenkins 上配置一个任务定时拉取最新代码执行 pytest 命令生成 Allure 报告并在构建结果里展示。设备可以选择宿主机连接的 Android 真机也可以用 Docker 里的模拟器方案不过模拟器的可维护性通常不如一台长期通电的真机稳定。如果资金和网络条件允许也可以接入云测平台把用例跑在远程设备池上一次性覆盖多种机型但脚本要提前处理好设备分辨率差异和系统版本差异。还有一点想单独提一下就是测试脚本本身的“代码质量”和业务代码一样需要维护。元素定位的变化、页面流程的调整、测试数据的过期都会让用例翻车。我见过很多团队一开始搭自动化跑得很欢半年后没人维护用例挂了一堆也不修最后整套体系废弃非常可惜。自动化测试的价值不是“搭出来”而是“持续稳定地提供回归信号”。所以每次 App 版本更新测试代码的评审和更新应该跟业务代码同期进行而不是等用例挂了再救火。关于 Appium 的未来扩展方向比较实际的有三条路一是把 Appium 用例和接口自动化测试结合在登录、数据准备这些环节调用真实接口完成前置条件能省不少时间二是引入图像识别作为兜底断言比如用 OpenCV 比对界面截图弥补控件属性缺失的场景三是基于 AI 的方式让工具自动生成一些基础的 UI 操作脚本这条路目前还在快速迭代里不建议还没把基础功底打牢就追这个风口。说到底工具只是杠杆真正决定自动化项目成败的是你对业务的理解和对测试工程化的把控。最后分享一点实操心得。刚开始学 Appium 的时候别一上来就追求搭一个完美的框架先把最简单的一条用例从手工操作变成脚本跑通再逐步加等待、封装、断言、报告、批量执行这样每一步的反馈都很及时也知道每一步解决了什么问题。移动端自动化这条路很宽但它的第一步从来都不难就藏在你第一次成功跑起来的那个会话里。
返回列表