资讯动态

Appium自动化测试入门:从零搭建第一个Python脚本并跑通计算器用例

发布时间:2026/10/15 10:53:24 来源:尧图企业网站定制
1. 从零到一为什么第一个脚本值得你花两小时死磕很多人学 Appium 卡在第一步不是环境装不上就是脚本跑起来报一堆看不懂的错。我带过不少新人发现一个规律第一个能跑通的自动化脚本价值远超后面十个复杂用例。因为它打通的是整条链路——从 Python 调用 Appium 客户端库到 Appium Server 接收指令再通过底层驱动把操作翻译成手机能识别的动作最后把结果回传。这条链路任何一环断了脚本就是一堆死代码。这篇内容面向的是已经装好 Python、Appium Server、Android SDK 和模拟器或真机的朋友。如果你还没装好建议先把环境搞定再回来因为脚本报错时你分不清是环境问题还是代码问题排查成本会翻倍。我会用一个最经典的场景——打开手机上的计算器应用做一次加法运算并验证结果——把第一个脚本从设计思路到落地实现完整走一遍。选计算器不是因为它简单而是因为它足够稳定、无需登录、无需网络能把干扰因素降到最低让你专注在 Appium 的核心机制上。你可能会问为什么不用微信、淘宝这类应用练手我的经验是新手第一个脚本一定要选系统自带应用。第三方应用有启动广告、权限弹窗、版本更新导致的元素变化这些都会让你在还没理解 Appium 工作原理之前就被各种意外打乱节奏。计算器、设置、时钟这类系统应用元素结构稳定启动速度快是练手的最佳选择。2. 脚本设计前的三个关键决策2.1 定位策略为什么我优先推荐 accessibility_idAppium 支持多种元素定位方式id、xpath、class name、accessibility_id、uiautomator 等等。新手最容易上手的是 xpath因为它直观能直接看到层级结构。但我实测下来第一个脚本应该优先用 accessibility_id原因有三。第一accessibility_id 在 Android 上对应的是元素的 content-desc 属性这个属性本来就是为无障碍功能设计的系统应用里几乎每个可交互元素都有稳定性极高。第二它不受界面层级调整的影响而 xpath 只要中间多套一层布局路径就失效了。第三accessibility_id 的查找速度比 xpath 快一个数量级因为它是直接属性匹配不需要遍历整个 DOM 树。当然accessibility_id 不是万能的。如果目标元素没有 content-desc你就得退回到 id 或 xpath。我的建议是定位优先级依次为 accessibility_id id class name xpathxpath 作为最后手段因为它最脆弱。2.2 等待机制隐式等待和显式等待怎么选脚本跑不起来十有八九是等待没做好。Appium 有两种等待方式隐式等待implicitly_wait和显式等待WebDriverWait。很多教程只讲隐式等待设一个全局的 10 秒就完事但实际项目中这样做会埋雷。隐式等待的逻辑是在查找元素时如果没立刻找到就轮询等待直到超时。它的问题是对所有元素一视同仁一个本来就不存在的元素也要等满超时时间拖慢整体执行速度。显式等待则是针对特定条件轮询比如“等待某个元素可见”“等待某个元素可点击”条件满足立即继续不满足才等到超时。我的做法是隐式等待设一个较短的兜底值比如 5 秒关键步骤用显式等待精确控制。这样既不会因为某个元素加载慢而误报也不会让脚本无谓地空等。第一个脚本里我会在启动应用后用显式等待确认首页加载完成后续操作则依赖隐式等待兜底。2.3 断言设计没有断言的自动化脚本等于白跑新手写脚本最容易忽略的就是断言。脚本点了一通按钮最后打印个“执行完毕”这根本不叫自动化测试叫自动化操作。测试的核心是验证结果是否符合预期所以每个关键步骤后都要有断言。断言分两种硬断言和软断言。硬断言失败直接抛异常终止脚本适合关键路径软断言失败记录错误但继续执行适合批量验证。第一个脚本我建议用硬断言因为流程简单一旦某步失败后面继续跑也没意义。断言的内容要具体比如计算器做 35断言结果必须是“8”而不是“结果不为空”这种模糊判断。3. 环境确认与前置检查清单3.1 启动 Appium Server 的正确姿势在写代码之前先把 Appium Server 跑起来。命令行输入appium即可启动默认监听 4723 端口。如果你看到类似 “Appium REST http interface listener started on 0.0.0.0:4723” 的输出说明服务正常。这里有个坑Appium 1.x 和 2.x 的启动参数差异很大。2.x 版本把驱动拆分成独立的插件需要额外安装 uiautomator2 驱动。如果你用的是 2.x启动前要确认驱动已装好否则脚本连接时会报 “No driver found” 之类的错误。检查方法是运行appium driver list --installed看到 uiautomator2 才算就绪。另一个常见问题是端口占用。4723 被其他程序占了Appium 会启动失败或换端口。我习惯在启动时显式指定端口比如appium -p 4725然后在脚本里对应改掉连接地址避免和其他工具冲突。3.2 获取应用包名和启动 ActivityAppium 需要知道你要操作哪个应用这靠两个参数appPackage包名和appActivity启动页 Activity。获取方法有几种最直接的是用命令行adb shell dumpsys window | grep mCurrentFocus这行命令会输出当前前台应用的包名和 Activity。先手动打开计算器再执行这条命令就能拿到你需要的信息。注意不同手机厂商的计算器包名不一样模拟器上通常是com.android.calculator2真机上可能是com.xxx.calculator以实际输出为准。还有一种方法是用adb shell pm list packages列出所有包名配合 grep 过滤。但这个方法只能拿到包名拿不到 Activity所以还是推荐用 dumpsys 的方式。提示部分系统应用的启动 Activity 带有多进程或 splash 页直接启动可能停在过渡页。遇到这种情况可以在启动后加一个显式等待等真正的首页元素出现再继续操作。3.3 设备连接状态确认脚本跑之前用adb devices确认设备已连接。输出里应该有一行设备序列号和 “device” 状态。如果是 “unauthorized”说明手机上还没授权 USB 调试需要在手机上点允许。如果是 “offline”通常是 adb 服务卡了执行adb kill-server再adb start-server一般能解决。模拟器的话确保模拟器已经完全启动到桌面不要停在开机动画阶段。我遇到过好几次脚本报连接失败最后发现是模拟器还没启动完就开始跑脚本了。4. 第一个脚本的完整实现与逐行拆解4.1 基础版脚本20 行代码跑通全流程先上代码然后逐段解释。from appium import webdriver from appium.options.android import UiAutomator2Options from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC options UiAutomator2Options() options.platform_name Android options.platform_version 13 options.device_name emulator-5554 options.app_package com.android.calculator2 options.app_activity .Calculator options.automation_name UiAutomator2 options.no_reset True driver webdriver.Remote(http://127.0.0.1:4723, optionsoptions) driver.implicitly_wait(5) wait WebDriverWait(driver, 10) wait.until(EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, 3))) driver.find_element(AppiumBy.ACCESSIBILITY_ID, 3).click() driver.find_element(AppiumBy.ACCESSIBILITY_ID, 加).click() driver.find_element(AppiumBy.ACCESSIBILITY_ID, 5).click() driver.find_element(AppiumBy.ACCESSIBILITY_ID, 等于).click() result driver.find_element(AppiumBy.ID, com.android.calculator2:id/result).text assert result 8, f预期结果是 8实际得到 {result} driver.quit()这段代码看起来简单但每一行都有讲究。UiAutomator2Options是 Appium 2.x 推荐的配置方式比老式的字典传参更清晰还能做类型校验。no_resetTrue表示不重置应用状态这样每次跑脚本不会清掉应用数据启动更快。如果你希望每次都是干净环境把它设为 False。webdriver.Remote的第一个参数是 Appium Server 地址第二个参数是配置对象。注意 2.x 版本里 options 是关键字参数不是位置参数写错了会报类型错误。4.2 元素定位的实操细节计算器的数字按钮accessibility_id 就是数字本身比如 “3” 对应 content-desc“3”。但运算符按钮不一样加号的 content-desc 可能是 “加” 也可能是 “plus”取决于系统语言。这就是为什么我强调第一个脚本要用系统应用并确认语言环境。如果你的模拟器是英文系统就得把 “加” 改成 “plus”“等于” 改成 “equals”。结果展示区的定位用的是 id因为那个 TextView 没有 content-desc。id 的完整写法是包名:id/控件名这个可以通过 Appium Inspector 或者 uiautomatorviewer 查看。我习惯在定位前先用 Inspector 确认一遍避免猜错。注意不同 Android 版本的计算器界面结构可能不同result 这个 id 不一定通用。如果找不到用 Inspector 重新抓一下换成实际的 id 或 xpath。4.3 断言与清理断言那行是整个脚本的灵魂。assert result 8如果失败会抛出 AssertionError后面的driver.quit()就不会执行导致 Appium 会话残留。所以更稳妥的写法是用 try-finallytry: # 所有操作和断言 assert result 8, f预期结果是 8实际得到 {result} finally: driver.quit()这样无论断言成功还是失败会话都会被正确关闭。我见过太多人脚本报错后没关会话下次跑的时候端口被占折腾半天。5. 常见报错与排查速查表5.1 连接类错误报错信息原因解决方法ConnectionRefusedErrorAppium Server 没启动或端口不对确认 appium 已运行端口与脚本一致No driver foundAppium 2.x 缺少 uiautomator2 驱动执行 appium driver install uiautomator2Device not found设备未连接或 adb 异常adb devices 确认必要时重启 adb 服务连接类错误最好排查因为报错信息很直接。我遇到最多的是端口冲突尤其是同时开了多个工具的时候。养成习惯启动 Appium 时显式指定端口脚本里写死同一个端口别用默认值碰运气。5.2 元素定位类错误NoSuchElementException是新手遇到最多的错误。原因通常有三种元素还没加载出来、定位方式写错了、页面根本不在预期界面。排查顺序应该是先加显式等待看是不是加载慢再用 Inspector 确认定位表达式是否正确最后截图看当前页面到底是什么。Appium 有个很实用的方法driver.save_screenshot(debug.png)报错时截个图一眼就能看出页面状态。还有一种隐蔽的情况元素存在但不可点击。比如按钮被遮挡、或者处于 disabled 状态。这时候click()会报ElementNotInteractableException。解决办法是等元素可点击用element_to_be_clickable条件或者先滚动到可见区域。5.3 会话类错误SessionNotCreatedException通常出现在启动阶段原因可能是 appPackage 或 appActivity 写错了也可能是设备上根本没装这个应用。先用adb shell pm list packages | grep calculator确认应用存在再用 dumpsys 确认 Activity 名称。另一个常见的是InvalidSessionIdException意思是会话已经结束了但脚本还在操作。这通常是因为前面的操作触发了应用崩溃或闪退。遇到这种情况先手动打开应用看是否正常再检查脚本里有没有误触退出按钮。6. 从能跑到好用三个进阶优化方向6.1 配置外部化第一个脚本跑通后下一步就是把硬编码的配置抽出来。设备名、平台版本、包名这些信息写死在代码里换台设备就跑不了。我的做法是放到一个 YAML 或 JSON 文件里脚本启动时读取。这样同一套代码可以在模拟器和真机之间切换只需要改配置文件。import yaml with open(config.yaml, r, encodingutf-8) as f: config yaml.safe_load(f) options.platform_version config[platform_version] options.device_name config[device_name]这个改动不大但为后续维护省了很多事。尤其是团队协作时每个人本地环境不同配置文件各自维护代码保持统一。6.2 封装常用操作点击、输入、等待这些操作每个脚本都写一遍太啰嗦。封装成工具函数既减少重复代码也方便统一加日志和异常处理。def click_by_accessibility_id(driver, value, timeout10): wait WebDriverWait(driver, timeout) element wait.until(EC.element_to_be_clickable((AppiumBy.ACCESSIBILITY_ID, value))) element.click()这样调用的时候一行搞定而且等待逻辑内置了不会漏写。封装的时候注意别过度抽象把简单问题复杂化。我的原则是同一个操作重复三次以上才封装。6.3 引入测试框架单脚本跑没问题后建议接入 pytest 或 unittest。框架能帮你管理用例、生成报告、处理前置后置操作。pytest 的 fixture 机制特别适合 Appium可以把 driver 的创建和销毁放在 fixture 里每个用例自动获得干净的会话。import pytest pytest.fixture(scopefunction) def driver(): options UiAutomator2Options() # ... 配置 drv webdriver.Remote(http://127.0.0.1:4723, optionsoptions) yield drv drv.quit()这样每个测试函数只需要接收 driver 参数不用关心启动和关闭。scope 设为 function 表示每个用例都重启应用设为 session 则所有用例共用一个会话启动更快但状态可能互相影响。第一个项目我建议用 function稳定优先。7. 我在实操中踩过的坑与经验总结第一个脚本我前前后后调了差不多三个小时大部分时间花在排查环境问题上。印象最深的一次是脚本一直报找不到元素用 Inspector 看明明存在最后发现是模拟器分辨率和我截图看的不一样元素在屏幕外。所以定位之前一定要确认元素在当前可视区域内必要时先滚动。还有一次是断言一直失败结果是计算器显示的是 “8.0” 而不是 “8”。这种格式化差异在数值计算里很常见断言时要么做类型转换要么用包含匹配。我现在的习惯是断言前先 strip 一下去掉首尾空格再做比较。关于等待时间我的经验值是应用启动等待 10 秒页面切换等待 5 秒元素查找等待 3 秒。这些不是死规定要根据实际设备性能调整。模拟器慢就加长真机快就缩短。关键是别用一个全局的超大等待值糊弄那样脚本跑起来会非常慢。最后说一个心态问题。第一个脚本报错是必然的不报错才奇怪。遇到报错别急着搜答案先看报错信息里的关键词定位到是连接问题、定位问题还是会话问题然后按类别排查。大部分错误在报错信息里已经说得很清楚了只是新手容易被一长串堆栈吓到。把堆栈从下往上看最后一行往往是根因。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑