刚接触移动端自动化测试的时候我绕了不小的弯路才真正把Appium用起来。这工具在业内的口碑很分裂一方面它是移动应用自动化测试领域的“标配”另一方面新手上路时光是环境搭建和元素定位就能把热情消磨殆尽。今天这篇不整那些花活就按我自己走通过的路径来聊——Appium环境怎么装、Appium Inspector怎么用、xpath/id/accessibility id到底怎么选、第一个脚本怎么写、跑起来之后有哪些坑。适合刚入门的测试工程师或者被环境配置劝退、想重新上路的开发者参考。1. 先搞清楚Appium到底解决什么问题再决定要不要学1.1 Appium不是“测试工具”而是一个“协议翻译官”很多人第一次接触Appium以为它是一个类似Selenium那样的“控制工具”这种理解不算错但会限制你的排查能力。我更喜欢把它理解为Appium Server本身不执行任何UI操作它只负责“翻译”。具体来说Appium基于WebDriver协议。你在Python、Java、JS等任意语言里写driver.find_element、driver.click()这些调用会通过HTTP请求发给本地启动的Appium ServerAppium Server再把指令转成对应平台能听懂的原生驱动命令——Android上走UIAutomator2或EspressoiOS上走XCUITest。说白了Appium是一层“通用遥控器”你按遥控器上的按钮真正去点电视、换台的是背后的驱动。这也是为什么它能做到“一份脚本两套平台”只要你的脚本用的是Appium抽象出来的API底层驱动换了代码基本不用动。理解这一层后面遇到任何诡异报错你至少能判断问题是出在“遥控器”还是在“电视机”。1.2 跨平台方案这么多为什么大家默认选Appium移动自动化的选项其实不少。Android原生有Espresso、iOS原生有XCUITest近些年还有Maestro、Airtest这类新工具。但Appium能成为默认选项靠的是三个优势跨平台Android和iOS同一套API。公司如果两个端都要回归这是最现实的需求。多语言不绑定某一种语言。团队Python熟就写PythonJava熟就写Java甚至Node.js都能写不存在“为了自动化重学一门语言”的额外成本。应用类型覆盖广原生App、混合AppWebView页面、H5页面Appium都能测。这一点在实际项目里非常重要因为现在几乎没有哪款App是100%纯原生。当然它也不是银弹。如果你只测Android一个平台并且团队愿意为极致稳定性投入Espresso那种直接在App进程里跑的方案反而更快、更稳。Appium的代价是中间隔了一层Server执行速度会慢一些在秒级操作的场景下体感差别不大但跑大规模用例时会有差距。1.3 先想清楚你的项目真的需要Appium吗我见过不少团队为了“上自动化”而上自动化最后脚本写了一堆维护成本却比手工测试还高。在决定动手之前建议你先对号入座适合用Appium的场景跨平台回归测试、端到端冒烟用例、需要接CI/CD的自动化流水线、真机多机型兼容跑测。不太适合的场景纯单元测试那是JUnit/Pytest的事、接口性能压测用不上UI层、需要像素级截图对比的视觉回归Appium能做但很麻烦。还有一个现实问题如果你们的App连UI元素标识resource-id、content-desc都没有规范会自动化的难度会直线上升。这一点后面会展开但请你先有个心理准备——Appium入门一半功夫在环境另一半功夫在跟开发沟通“把元素标识补全”。2. 环境搭建全记录从JDK到Appium Server一口气配完2.1 环境清单版本选错后面全是坑环境搭建劝退了至少一半新手。其实东西不多但每个版本都容易踩坑。我按Android主线来列macOS和Windows都适用只是环境变量的配置方式不同组件推荐版本说明JDK11或17Android Gradle插件要求JDK 11Appium官方对JDK版本没有硬性要求Android SDK最新稳定版建议直接装Android Studio顺手把Android SDK拿到手Node.js14或更高LTSAppium 2.x是Node.js项目LTS版本最稳Appium Server2.x最新版通过npm install -g appium安装Appium Driveruiautomator2Appium 2.x要把驱动单独装一次这是很多老教程没提到的地方appium-doctor最新版环境自检工具推荐装Python3.9如果你的脚本打算用Python写还需要pip install Appium-Python-Client为什么Appium 2.x要单独装驱动这是跟1.x最大的区别。1.x把Android/iOS的驱动全部打在一个包里2.x改成按需安装。好处是安装包更小、升级互不影响坏处是老教程没这一步导致很多人卡在“启动session时报错找不到驱动”。装完Appium之后记得跑一句appium driver install uiautomator2这步不做后面就等着报“Could not find a driver for platformName Android”吧。iOS驱动同理但需要macOSXcode。2.2 模拟器还是真机第一次跑通用哪个更省心我的建议是第一次跑通优先用模拟器AVD。原因很简单——真机需要插线、开USB调试、处理驱动冲突任何一个环节出问题都会打断你学习节奏。而模拟器只要Android Studio里创建好AVD启动后就是一个干净的Android环境。创建模拟器时有几个细节容易被忽略建议选带Google APIs的系统镜像不要选纯AOSP镜像很多App依赖Google Play服务虽然没有也能运行但容易出莫名问题。启动模拟器后把动画缩放关掉设置-开发者选项-窗口动画缩放、过渡动画缩放、动画程序时长缩放都改成“关闭”或0.5x。这些动画会导致元素定位时机不稳定增加等待的不确定性。确保用adb devices能看到设备。模拟器启动后自动注册一般不用手动adb connect但如果用Genymotion或第三方模拟器可能需要手动连接adb connect 127.0.0.1:5555。真机的部分我也说一句手机打开“开发者选项”和“USB调试”插线后在手机上选择“允许USB调试”。如果adb devices显示unauthorized大概率是手机上没确认弹窗重新拔插一次就好。真机测试碎片化问题很严重入门阶段没必要上来就死磕。2.3 环境变量核心就四个配完重开终端很多教程会把环境变量讲得很玄其实对Appium来说核心就四样# Windows在系统属性-环境变量里配置 ANDROID_HOME C:\Users\你的用户名\AppData\Local\Android\Sdk JAVA_HOME C:\Program Files\Java\jdk-11 # PATH追加 %ANDROID_HOME%\platform-tools %ANDROID_HOME%\tools %JAVA_HOME%\bin# macOS/Linux打开 ~/.zshrc 或 ~/.bash_profile 添加 export ANDROID_HOME$HOME/Library/Android/sdk export JAVA_HOME$(/usr/libexec/java_home -v 11) export PATH$PATH:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$JAVA_HOME/bin配置完成后一定要重开终端或注销重进让环境变量生效。然后验证java -version node -v adb version appium -v这四条命令任何一个报“command not found”说明对应环境变量没配好。顺序排查即可。顺便说一句如果adb能找到但appium找不到多半是Node的全局bin目录没进PATH用npm config get prefix查看之后把它加进去。2.4 用appium-doctor一次确认别等脚本跑了才报错配完环境强烈建议先跑一次体检appium-doctor --android它会逐项检查JDK、Android SDK、ANDROID_HOME、Node等关键环境。绿色PASS就是正常黄色WARN说明某组件异常但可能不致命红色FAIL说明缺东西。比如常见的Androi SDK相关WARN可能是因为没装某些系统组件但如果你只是做App自动化一般不影响。第一次跑完我建议把红色FAIL都处理掉再往下走省得后面报错时两头排查。环境这块你可能会觉得步骤多但回想一下真正需要动手的就三件事装JDK、装Android SDK、装Node并安装Appium。后面大部分时间其实是在等下载和重开终端。3. Appium Inspector把元素定位从“猜”变成“看”3.1 为什么元素定位决定自动化测试的生死自动化测试的本质可以拆成三步找到元素、操作元素、验证结果。看起来简单但实际跑起来80%的失败都发生在“找不到元素”这一步。脚本写的对不对断言准不准倒还是其次——你连登录按钮都点不到后面全是空谈。所以Appium Inspector这个工具在你入门阶段比代码本身更重要。它相当于给你配了一双“透视眼”把App当前页面的UI层级、每个元素的位置、属性、文本全部摊开在你面前。有人说“Appium Inspector可以获取xpath、id、accessibility id等元素定位信息”这话说得没错但这只是它的能力之一。它更大的价值是帮你建立“元素长什么样”的直觉知道该拿哪个属性去定位。3.2 用Appium Inspector看页面元素的具体流程第一步先启动Appium Server。终端里跑一句appium看到类似Appium server listening on 0.0.0.0:4723就说明服务起来了。第二步打开Appium Inspector。Appium 2.x桌面版自带Inspector入口直接在Appium Desktop或命令行工具里找到“Start Session”按钮。新版界面长这样左边是配置区右边是连接按钮。填的配置就是后面脚本里要用的Desired Capabilities格式如下{ platformName: Android, appium:platformVersion: 12.0, appium:deviceName: emulator-5554, appium:appPackage: com.example.app, appium:appActivity: .MainActivity }注意Appium 2.x里自定义capability要加appium:前缀这是跟1.x时代不同的细节不加的话部分capability不生效。填好后点击“Start Session”Appium会启动你的App并在Inspector里显示当前页面的截图。点击截图上的任意元素左边会同步显示它的全部属性resource-id对应代码里的By.IDcontent-desc对应accessibility idtext元素文本class元素类型如android.widget.Buttonbounds元素在屏幕上的坐标范围这些属性就是元素定位的全部“弹药”。3.3 元素定位策略怎么选id优先但别只靠id我整理了一份定位策略优先级按稳定性和效率排序策略写法示例稳定性说明resource-ididBy.ID, com.example.app:id/btn_login高Android原生最推荐的定位方式类似网页开发里的id只要开发不随意改名基本不会挂accessibility idBy.ACCESSIBILITY_ID, 登录按钮高对应content-desc属性跨Android/iOS通用对无障碍测试友好xpathBy.XPATH, //android.widget.Button[text登录]中灵活但执行慢最后手段class nameBy.CLASS_NAME, android.widget.Button低基本不能单独用页面上同类元素太多textBy.XPATH, //*[text登录]中实际是xpath的一种文本变了就挂为什么id优先因为它稳定。只要开发不随意改resource-id页面布局怎么调都不影响定位。但现实情况是很多App的resource-id是自动生成的随机字符串每个版本都会变。这时候你要么求开发加上稳定的id要么改用其他属性兜底。accessibility idcontent-desc是Google推荐的无障碍属性很多App不太重视但它是跨平台自动化里最“文明”的元素标识。你甚至可以说服开发人员做无障碍支持不只是为了自动化更是为了视障用户能正常使用App。这个理由很正当一般开发都愿意配合。3.4 xpath常用写法看这一份就够了xpath是最终的兜底方案它的好处是“只要肉眼可见的元素总能写出定位表达式”坏处是比id慢而且写不好容易依赖页面结构页面一调整就挂。新手最容易犯的错误是直接复制Inspector自动生成的绝对路径类似这种//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Button[1]这种路径又长又脆中间任何层级发生变化都直接失效。正确姿势是写相对路径从元素的属性出发# 按文本定位 //android.widget.Button[text登录] # 按resource-id定位用resource-id //android.widget.EditText[resource-idcom.example.app:id/et_username] # 按content-desc定位 //android.widget.TextView[content-desc用户名输入框] # 按部分匹配定位页面动态拼接文本时很好用) //android.widget.Button[contains(text, 登)] # 多属性组合定位 //android.widget.Button[text登录 and resource-idcom.example.app:id/btn_login]写xpath的两个要点尽量从“元素自身属性”出发不要带层级序号。判断不了唯一性时先用//android.widget.Button枚举页面上的同类元素再挑选最独特的那个属性。另外提一句xpath的执行速度在Android上xpath查询需要遍历UI层级数页面越复杂越慢。所以真到了性能瓶颈时能少用xpath就少用。3.5 元素定位失败时的排查顺序元素找不到是新手最崩溃的时刻。我的排查顺序是这样的先确认页面真的加载完了。App启动前几秒UI树可能还没构建完先在Inspector里手动刷新一次或者用代码加显式等待。检查caps对不对。有时候是启动了错的Activity或者App闪退回桌面定位的当然不是你想要的页面。核对属性是否准确。Inspector里看到的resource-id、text、content-desc和你代码里写的、例如多了空格或大小写不一致就会找不到。文本框的text属性经常有空格最坑的是你看不到。确认是不是WebView。如果页面是H5页面原生定位方式是找不到的要切换context到WebView再操作这个后面会专门说。考虑动态属性。id是随机字符串、文本频繁变化时换xpath的contains或combination方式兜底。实在定位不到时还有个土办法打开手机的“开发者选项-显示布局边界”肉眼看看UI层级里有没有被遮挡的透明元素。有时候你以为点在登录按钮上实际上被一个透明的View拦截了这种情况用xpath按坐标硬找是没用的得从布局结构上找原因。4. 跑通第一个脚本从Desired Capabilities到点击、输入、断言4.1 Desired Capabilities详解Appium启动App的“指令卡”环境准备好了元素定位也有谱了开始写脚本。第一个关键是Desired Capabilities——它就是告诉Appium“你要测什么设备、哪个App、怎么启动”的指令卡。常用capabilities说明如下Capability作用示例platformName平台名称AndroidplatformVersion系统版本12.0deviceName设备名称emulator-5554不强制真实型号appPackageApp包名com.example.appappActivityApp启动Activity.MainActivityapp安装包路径/path/to/app.apk与上面二选一noReset是否重置App状态True保留登录态或FalseautomationName底层驱动UiAutomator2Android默认autoGrantPermissions自动授权权限True省去手动处理弹窗这里新手最容易卡住的是appPackage和appActivity。如果你已经装好了要测的App可以用一条命令查# 查看当前前台App的包名和Activity adb shell dumpsys window | grep mCurrentFocus命令行工具aapt也可以查安装包的启动Activity如果你手里有apkaapt dump badging app.apk | grep launchable-activity4.2 最小可运行脚本Python Appium 2.x我推荐新手用Python写第一版脚本代码量最小、读起来最直白。先把客户端库装好pip install Appium-Python-Client然后建一个first_test.py。下面这个脚本做的事情是启动App等待登录按钮出现点击它然后在登录页输入用户名和密码最后断言页面上是否出现了“登录成功”提示from appium import webdriver from appium.options.android import UiAutomator2Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from appium.webdriver.common.appiumby import AppiumBy def main(): options UiAutomator2Options() options.platform_name Android options.device_name emulator-5554 options.app_package com.example.app options.app_activity .MainActivity options.automation_name UiAutomator2 options.no_reset True driver webdriver.Remote(http://127.0.0.1:4723, optionsoptions) try: # 显式等待等登录按钮出现再点击 login_btn WebDriverWait(driver, 10).until( EC.element_to_be_clickable( (AppiumBy.ID, com.example.app:id/btn_login) ) ) login_btn.click() # 输入用户名、密码 username driver.find_element( AppiumBy.XPATH, //android.widget.EditText[content-desc用户名输入框] ) username.send_keys(test_user) password driver.find_element( AppiumBy.ID, com.example.app:id/et_password ) password.send_keys(123456) # 点击登录 driver.find_element( AppiumBy.ID, com.example.app:id/btn_submit ).click() # 断言登录成功提示出现 success_toast WebDriverWait(driver, 5).until( EC.presence_of_element_located( (AppiumBy.XPATH, //*[contains(text, 登录成功)]) ) ) print(断言通过登录成功提示可见) finally: driver.quit() if __name__ __main__: main()解释几个关键点options.no_reset True表示不重置App数据比如不重新走引导页。首跑时建议先改成False跑一遍让App处于初始状态。driver.find_element是同步操作找不到元素立刻抛异常所以最好配合显式等待使用。Appium 2.x的webdriver.Remote(http://127.0.0.1:4723, optionsoptions)第二个参数直接传UiAutomator2Options比传capabilities字典更规范。4.3 首次运行最常见的三个报错和解决方案头次跑脚本没人能不踩坑。我把最高频的三个报错和排查链路列出来报错一“Could not find a connected Android device”Drivers的报错信息一般是Could not find a connected Android device或者adb: more than one device/emulator。前者说明adb devices里没设备后者说明插了多台设备但没指定udid。解法先adb devices确认设备在线多设备时在options里加上options.udid emulator-5554。报错二“A new session could not be created”片头一大段日志核心可能是appPackage和appActivity配错、App没安装、或者Driver没装。先用adb shell dumpsys window | grep mCurrentFocus拿到正确包名Activity如果是Driver问题用appium driver list检查uiautomator2是否已安装。报错三“Element not found”定位表达式写错或者页面没加载完。先按第3节的排查顺序走一遍特别注意text里看不见的空格和动态id。4.4 加断言才算一个完整的用例很多入门脚本写到最后就是“点击一下、输入一下”然后verbose日志一打印就觉得完事了。其实没有断言的自动化用例是没有意义的——它只能证明“操作没报错”但无法证明“功能是对的”。断言的本质是“用代码验证结果”。最简单的断言就是确认某个元素是否存在、是否可见比如上面代码里检查“登录成功”提示是否出现。进阶的断言可以读元素文本做比对text driver.find_element(AppiumBy.ID, com.example.app:id/tv_result).text assert text 登录成功, f期望登录成功实际为{text}记住一个用例的完整生命周期是“前置准备 - 执行操作 - 断言结果 - 清理恢复”。前三步大家都会写最后一步“清理恢复”往往被忽略导致用例之间的状态互相污染。这个我们放到下一节细说。5. 从“能跑”到“稳定跑”那些文档里不会写的实战坑5.1 等待策略sleep一时爽维护火葬场新手最常见的写法是time.sleep(3) driver.find_element(...).click()跑一次能过换一台启动稍慢的手机就挂。我见过最夸张的是有人为了稳定把所有操作前都塞了sleep(5)跑完一个用例要一分钟。这本质上是用“等待时长”去赌“页面加载时间”非常脆弱。正确做法是显式等待等某个条件满足才继续执行。前面代码里用的WebDriverWait expected_conditions就是标准姿势。针对不同的操作等待条件也要变等元素可点击EC.element_to_be_clickable等元素存在EC.presence_of_element_located等元素可见EC.visibility_of_element_located等文本变化EC.text_to_be_present_in_element显式等待的超时时间建议10秒左右轮询间隔不用调整默认即可。这是自动化稳定性提升最明显的一步没有任何理由不用。5.2 弹窗、权限请求、Toast消息怎么处理Android系统级弹窗是自动化的大麻烦——你永远不知道App启动时会弹“定位权限”“通知权限”“广告弹窗”还是“更新提示”。最省事的办法是在capabilities里加一项options.auto_grant_permissions True这会自动授权运行时权限能省掉一大半权限弹窗。但像“广告弹窗”“版本更新弹窗”这类应用内弹窗autoGrantPermissions管不了必须在用例开始或结束前统一处理。常见做法是写一个“关弹窗”函数专门识别弹窗上的“关闭”“跳过”“以后再说”按钮并点击def close_ads(driver): try: close_btn WebDriverWait(driver, 3).until( EC.element_to_be_clickable( (AppiumBy.XPATH, //*[text关闭 or text跳过 or text以后再说]) ) ) close_btn.click() except Exception: # 没有弹窗就忽略 passToast消息的处理也有关键点。Toast不是常规View元素用driver.find_element经常找不到但用xpath可以toast WebDriverWait(driver, 5).until( EC.presence_of_element_located( (AppiumBy.XPATH, //android.widget.Toast[contains(text, 网络错误)]) ) )注意Toast只会停留几秒等待超时设短一点断言到了就行。5.3 测试数据隔离避免用例之间互相污染自动化用例跑多了最头疼的是“没有任何逻辑问题但用例突然挂了”——通常就是数据污染。比如用例A登录了一个账号用例B跑的时候发现上次登录态还在结果点击按钮跳的页面完全不一样。数据隔离的基本策略有三个每个用例启动前重置App状态用adb shell pm clear 包名清除App数据。这比卸载重装快也比重启系统干净。用独立测试账号多个用例并行时给不同账号避免同一账号状态互相影响。登录态是关键建议每个用例都先登出或清token。通过接口准备数据UI用例的重点是UI流程不是数据准备。能用接口造的数据就不要靠UI一步步点出来。比如你要测试“订单列表页”直接调后台接口造好订单数据App启动后就能看到比在UI上手动创建一个订单快得多。5.4 真机碎片化与稳定性减少对“坐标”的依赖移动自动化在真机上跑最大的敌人是碎片化。屏幕分辨率不同、系统版本不同、厂商ROM不同都会导致元素位置偏移。所以第一条铁律就是永远不要用坐标点去点击元素。# 反面教材按坐标点击换台手机大概率挂 driver.tap([(500, 1200)])坐标点击的问题在于它只认像素位置不认元素。同一台手机上能跑通换一台分辨率不同的手机按钮位置就变了。元素定位的关键属性resource-id和content-desc是跟着元素走的跟分辨率无关。真机上如果实在没有合适的元素属性宁可让开发在关键控件上加一个content-desc或testID也不要落到坐标点击。从项目长期维护角度讲这比什么都重要。5.5 日常维护建议早点引入Page Object别让脚本裸奔入门时写脚本很随意但一旦用例超过20个就会发现问题——页面结构一调整脚本里到处是硬编码的元素定位改起来想死。这时候要尽早引入Page Object模式。核心思想很简单每个页面写一个类页面上的元素定位和操作方法都收拢在这个类里。测试代码只调类的方法不直接碰定位表达式。这样页面改了只需要改对应Page类不用动每个用例。Appium的Page Object跟Selenium的WebDriver Page Object写法几乎一致网上资料很多入门阶段可以先自己手动写成工具函数不用上框架等体量大了自然知道该上什么。6. 进阶扩展手势、滑动、跨App和iOS入门之后还能做什么6.1 手势操作滑动、长按、缩放Appium能做的不仅是点击、输入、断言。真实业务里常有滑动翻页、上拉刷新、长按删除、双指缩放等操作。Appium 2.x推荐用W3C Actions实现。滑动的经典写法from appium.webdriver.common.touch_action import TouchAction # 简单滑动Appium 2.x仍兼容 driver.swipe(start_x500, start_y1400, end_x500, end_y800, duration500)更推荐用W3C Actions更接近标准from selenium.webdriver.common.actions.action_builder import ActionBuilder from selenium.webdriver.common.actions.pointer_input import PointerInput mouse PointerInput(PointerInput.TOUCH, touch) actions ActionBuilder(driver, mouse) actions.pointer_down(500, 1400) actions.move_to(500, 800, duration500) actions.release() actions.perform()长按操作在自动化里也很常用比如长按图标触发编辑模式。这类操作一旦掌握了W3C动作的套路其实本质就是“按下-停留-抬起”只是参数不同。6.2 混合App和H5页面你要能“切换上下文”现在很少有App是纯原生的大量页面用WebView内嵌H5。自动化测到这类页面时会发现用resource-id、class都定位不到元素——因为它们跑在WebView里对原生自动化来说是个“黑盒”。解决办法是切换到WebView上下文# 查看当前有哪些上下文原生的叫NATIVE_APP print(driver.contexts) # 切换到WebView driver.switch_to.context(driver.contexts[-1]) # 然后就能用Selenium的定位方式了 driver.find_element(AppiumBy.XPATH, //input[placeholder请输入手机号])注意WebView调试开关Android的WebView必须开启“WebView调试模式”Appium才能与之通信。混合App的自动化是另一个深坑入门阶段建议先明确页面类型把原生部分跑通之后再做H5部分。6.3 iOS入门迁移换汤不换药Android版本跑通之后再接触iOS心里就有底了。iOS环境需要Mac、Xcode和Appium的XCUITest驱动。capabilities略有差异{ platformName: iOS, appium:platformVersion: 17.0, appium:deviceName: iPhone 15, appium:bundleId: com.example.iosapp, appium:automationName: XCUITest }和Android最大的区别appPackage/appActivity变成了bundleIdaccessibility id的对应属性在iOS上可以直接对应元素的accessibilityIdentifier使用上比Android更顺滑。Appium Inspector在两个平台上的工作逻辑一模一样——你之前学的“查看元素属性 - 选定位策略 - 写脚本”的流程原封不动搬到iOS。6.4 入门之后可以往哪个方向走Appium入门只是起点后续能做的事情还有不少接入CI把Appium脚本放进Jenkins或GitLab CI每次代码提交自动跑冒烟回归。报告可视化集成Allure或自定义HTML报告把失败截图自动附到报告里。多设备并行通过Appium Grid或自建多端口方式让同一套用例跑在多个真机上缩短回归时间。与接口测试配合UI测试负责主流程接口测试负责功能覆盖两条腿走路。我在实际项目中的体会是Appium的价值不在于“跑通一个脚本”而在于把它打磨成团队里天天都能跑的东西。稳定性的提升是一个持续的过程——元素写好、等待写对、数据隔离干净比写十几个没人维护的脚本有用得多。最后分享一个我踩过多次坑后的习惯从一开始就要求开发给每个页面关键元素加content-desc或者稳定的resource-id并在代码评审阶段把“元素可测试性”当做一个验收点。很多自动化项目的失败不是因为测试工具不行而是因为被测产品从一开始就没给自动化的“抓手”。如果你正打算在团队里推动Appium先把这条写进规范能省掉未来的无数个夜班。