ARTICLE DETAIL

资讯详情

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

Appium移动端自动化测试入门:环境搭建、元素定位与脚本编写实战

Appium移动端自动化测试入门:环境搭建、元素定位与脚本编写实战 1. 为什么要选Appium聊聊我的入坑原因这两年移动端测试的活越来越重手工点来点去不仅效率低版本迭代一快就完全跟不上。我自己在测试开发这条路上摸爬滚打几年先后试过不少工具最后真正让我定下心来深耕的还是Appium。如果你正在接触移动应用自动化测试或者想把手里的测试工作系统化地做起来Appium几乎是个绕不开的选项。先说清楚Appium是什么。它是一个开源的移动端自动化测试框架支持iOS、Android、Windows等平台核心特点是一套代码多端复用——通过WebDriver协议与手机交互把你在电脑上写好的测试指令翻译成手机能听懂的操作。相比其他工具Appium的优势在于不依赖手机端安装任何额外Agent用的是系统自带的自动化引擎iOS的XCUITest、Android的UiAutomator2这意味着你测的是用户真正会拿到的那个App而不是某个被注入过的特殊版本。我在实际工作中选Appium还有几个很实在的理由。第一它跨语言Python、Java、Ruby、JS都能写团队用什么栈都能接上第二它生态成熟遇到问题随便一搜就有解决方案第三它支持真机、模拟器、云测平台灵活度很高。当然它也不是没有槽点比如环境配置确实烦慢的时候让人抓狂但这些后面我会一一讲怎么解决。这篇文章适合谁看如果你刚接触移动应用自动化测试或者写了几条用例但总感觉不太稳固想系统地把环境、定位、脚本、调优整条链路都摸明白那这篇文章就是给你准备的。我会从环境搭建开始一步步走到编写能跑的测试脚本最后把那些文档里查不到的坑也一并倒出来。2. 环境搭建的每一步都给你捋清楚2.1 先装什么、后装什么顺序很重要Appium的环境搭建是入门的第一道坎很多人就是倒在这一步的。我见过太多同事卡在明明照着教程装的为什么就是跑不起来大部分原因其实是安装顺序和版本匹配的问题。我的建议是先装依赖再装Appium本体最后配置手机和模拟器。首先是Java。虽然我们可以用Python写测试脚本但Appium的Android部分依赖Java环境来驱动UiAutomator2所以JDK必须装。我推荐装JDK 8或11太新的版本有时候反而会跟旧的SDK组件产生兼容问题。装完后在命令行执行java -version确认能正常输出版本信息。接下来是Android SDK。如果你只测iOS那可以跳过这步但绝大多数入门场景都是从Android开始的。SDK不一定要装Android Studio全家桶但至少需要有adb、build-tools、platform-tools这几个部分。装好后把ANDROID_HOME环境变量配好指向你的SDK目录。然后是Node.js。Appium Server本身是Node写的所以Node环境必须准备好。这里有个小建议Node版本不要追新装LTS长期支持版就好我遇到过在太新的Node版本上装Appium报错的情况。以上都准备好了就可以用npm来安装Appium本体了。现在官方推荐的是Appium 2.x版本跟早期的1.x在插件管理上有一些区别。安装命令很简单npm install -g appium装完执行appium --version能输出版本号就说明Server本体OK了。但别急着高兴Appium 2.x默认不包含各个平台的驱动你还得单独安装UiAutomator2驱动appium driver install uiautomator2这一步在国内网络环境下经常超时如果遇到失败可以配置镜像源再试。2.2 为什么建议装Appium Inspector而不是只用代码定位环境装好之后很多人会直接开始写脚本这是新手最容易走弯路的地方。你连页面上那个按钮长什么样、id是什么都不知道怎么写定位所以我强烈建议安装Appium Inspector——它是Appium官方提供的元素检查工具能直接连接到你的手机或模拟器把当前页面的UI层级结构像DOM一样展示出来。有了Inspector你可以直接查到元素的id、class、xpath、accessibility id等定位信息还能实时验证你的定位表达式能不能找到元素。这比一边写代码一边猜靠谱得多。Appium Inspector一般通过Appium Desktop安装或者在Appium 2.x里通过插件方式使用。注意Inspector连接手机之前手机必须已经开启USB调试并且当前页面的App处于激活状态。模拟器的话要确保已经启动且未被其他adb进程占用。我用Inspector的日常流程是这样的先启动Appium Server再打开Inspector填好Desired Capabilities设备名、平台版本、App包名等信息点击启动会话就能看到手机屏幕的实时映射和UI树了。查到一个元素后直接复制它的定位信息回到代码里用效率很高。3. Desired Capabilities跟你手机正确握手的暗号3.1 核心参数逐一解释Desired Capabilities可以理解成一份连接说明告诉Appium你要测什么设备、什么系统、什么App。这些参数要是写错了后面什么都白搭。我先把我最常用的一组参数拿出来并逐个说明含义caps { platformName: Android, appium:deviceName: emulator-5554, appium:platformVersion: 12.0, appium:app: /path/to/app.apk, appium:automationName: UiAutomator2, appium:noReset: True, appium:unicodeKeyboard: True, appium:resetKeyboard: True }platformName就是平台类型Android或iOS这个没什么悬念。deviceName看着是设备名实际上只要你保证adb能识别到设备这个值写什么影响不大我一般直接写emulator-5554或设备型号字符串。platformVersion就是Android版本注意不是手机的销售版本号而是系统设置里那个Android 版本的数值。app指向APK文件的绝对路径如果你已经安装了App也可以不传这个参数改用appPackage和appActivity来定位启动的界面。3.2 参数选错会遇到的典型坑automationName是我反复强调的参数。在Appium 2.x里Android平台默认就是UiAutomator2但如果你在跑老项目用了Appium 1.x的遗留代码不写这个参数时默认值可能是旧的UiAutomator导致一些新的API不可用或者定位方式不兼容。所以我的习惯是永远显式写上。noReset也很关键。它表示在测试前后不要重置App的数据。如果你测的是需要登录的App不写这个参数或者设为False每次启动都会回到初始状态登录状态就没了用例也就没法跑。我一般设为True让App保持我之前手动设置好的状态。unicodeKeyboard和resetKeyboard是用来解决中文输入问题的。模拟器默认的输入法可能不支持sendKeys输入中文这两个参数配合上adb shell ime set切换输入法才能稳定地往输入框里填中文。还有些细节比如测试iPad时有个udid参数Web测试时有browserName参数场景不同会有差异但你可以遇到一个学一个核心那几个先记住就够入门了。4. 元素定位Appium Inspector拿到的那几个信息到底怎么用4.1 五种最常用定位方式对比元素定位是自动化测试的核心技术活。你定位的稳定性直接决定了测试脚本能不能长期可靠地跑下去。用Appium Inspector能拿到的定位信息主要有五种id、class、xpath、accessibility id、text文本内容。在Android上资源的id是最优先的选择格式类似com.example.app:id/btn_login。它本身就是开发者给UI控件起的身份证号只要开发不改ID这条定位就是最稳的。XPath则是最灵活的你可以通过任意属性值组合来定位比如找一个文本为登录的Button但灵活也意味着脆弱UI一调整层级可能就挂了。accessibility id在Android上往往对应content-desc属性专门给无障碍功能用的如果你测的App无障碍做得规范这个定位方式也相当稳定。我把它们的稳定性、灵活性和适用场景整理成了一张对比表方便你参考定位方式稳定性灵活性适用场景id高低首选UI结构变化不影响accessibility id高低App有完善的无障碍属性时class中低同类型控件少时可用xpath中高高组合条件定位但结构变更容易失效text中中文本唯一且页面简洁时4.2 怎么写出健壮的定位表达式关于XPath很多新手听到就头疼其实不用怕。核心就一个思路从你能唯一辨认的属性入手组合起来。别去复制Inspector自动生成的那长串绝对路径那种写法几乎只有当下的UI结构能用稍微改一个层级就全废。我更推荐写相对定位比如el driver.find_element(xpath, //android.widget.Button[text登录])这句话的意思是在整个页面里找文本内容为登录的按钮。这种写法没有死板的层级依赖就算这个按钮往上挪了一层依然能找到。还有一个实用技巧是组合索引比如页面上有多个同名的控件可以这样写el driver.find_element(xpath, (//android.widget.TextView[text热门])[1])先定位所有文本为热门的TextView再取第一个。这里要注意索引从1开始不是0跟代码数组下标不一样这也是我刚入门时踩过的坑。4.3 等元素出现的正确姿势定位写好了不能马上操作。App页面加载是有延时的尤其是网络请求回来之后列表才渲染完。你一启动就急着找元素大概率迎头一个Element Not Found。我的做法是加显式等待等某个元素出现后再继续from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait WebDriverWait(driver, 15) login_btn wait.until( EC.presence_of_element_located((AppiumBy.ID, com.example.app:id/btn_login)) ) login_btn.click()WebDriverWait会每500毫秒检查一次元素是否出现最长等15秒超时就抛出异常。这就避免了盲目time.sleep()浪费时间或者等待不足导致随机失败。等待策略是整个测试稳定性的一个分水岭我后面会专门展开讲。5. 从零写一个能跑的登录测试脚本5.1 项目结构怎么组织才不混乱到了这一步你环境OK了定位信息也拿到了该写第一个真正的测试脚本了。我的建议是别一上来就写一个几百行的大文件先把结构搭好用pytest作为测试框架来组织。下面是我推荐的一个最小但完整的项目结构test_app/ ├── conftest.py # pytest的全局fixture ├── config.py # 设备配置和App路径 ├── pages/ # 页面对象一个页面一个类 │ ├── login_page.py │ └── home_page.py ├── tests/ # 测试用例 │ └── test_login.py └── requirements.txt很多新手觉得这样分太复杂但等用例规模上去了你就知道好处了。页面对象模式Page Object Model简称POM的核心思想是把每个页面的元素定位和操作方法封装在独立的类里测试用例本身只关心业务逻辑。比如登录页的login_page.py负责找到用户名输入框、密码输入框、登录按钮并提供login(username, password)方法测试用例只需要调用这个方法。哪天登录页UI改版了你只需要改LoginPage一个文件测试用例一行不用动。5.2 关键的登录测试用例一步一步写出来我们以经典的登录场景为例完整写一遍。先看conftest.py里的Driver管理。pytest的fixture机制非常方便我一般这么写import pytest from appium import webdriver from config import caps pytest.fixture(scopeclass) def driver(): driver webdriver.Remote(http://127.0.0.1:4723/wd/hub, caps) driver.implicitly_wait(10) yield driver driver.quit()这个fixture会在测试类运行前启动一个连接所有测试方法共用这个driver用完自动退出。这里的implicitly_wait(10)是设了一个全局的隐式等待每次找元素时如果找不到会在10秒内不断重试。隐式等待和显式等待可以搭配使用但注意不要叠加过大的数值否则定位失败的用例会拖得很久。登录页的页面对象可以这样写from appium.webdriver.common.appiumby import AppiumBy class LoginPage: def __init__(self, driver): self.driver driver def input_username(self, text): el self.driver.find_element(AppiumBy.ID, com.example.app:id/username) el.send_keys(text) def input_password(self, text): el self.driver.find_element(AppiumBy.ID, com.example.app:id/password) el.send_keys(text) def click_login(self): self.driver.find_element(AppiumBy.ID, com.example.app:id/btn_login).click() def login(self, username, password): self.input_username(username) self.input_password(password) self.click_login()最后是测试用例本身看整体怎么串起来import pytest from pages.login_page import LoginPage from pages.home_page import HomePage class TestLogin: def test_login_success(self, driver): login_page LoginPage(driver) login_page.login(testerexample.com, 123456) home_page HomePage(driver) assert home_page.is_logged_in(), 登录成功后应跳转到首页这里HomePage里需要实现一个is_logged_in()方法判断首页的一个特征元素是否存在。用断言来验证结果这就是自动化测试的骨架——操作、验证一步都不能少。有些人写完操作步骤就结束了不做断言那顶多叫脚本不叫测试。5.3 pytest命令行跑起来以及结果怎么看在项目根目录执行pytest -v tests/test_login.py-v参数会输出每个用例的执行细节。如果一切顺利你会看到一条PASSED。如果你想把结果输出成HTML报告可以用pytest-html插件pytest -v tests/test_login.py --htmlreport.html生成的报告里会有每个用例的执行时间、失败时的完整错误栈还有截图功能可以配置。我通常会配合--maxfail1参数用例失败太多时及时停住省得浪费时间pytest -v tests/test_login.py --htmlreport.html --maxfail16. 常见问题与排查技巧实录6.1 会话就是启动不了这是环境搭建后第一道拦路虎。现象通常是执行脚本时报Could not create a new session或者An unknown server-side error occurred。我的排查步骤是固定的先命令行里执行adb devices确认设备在列表里状态是device而不是offline然后执行adb shell echo ping确认设备响应正常最后看Appium Server的日志里面通常有详细的错误原因。一个高频原因是appPackage和appActivity写错了App包名和启动Activity可以用下面的命令获取adb shell pm list packages | grep -i 你的应用关键词 adb shell dumpsys window | grep mCurrentFocus第二条命令能拿到当前前台运行的Activity名称非常实用。6.2 元素定位间接性失败这是最磨人的问题。脚本今天跑得好好的明天一跑就提示找不到元素了。多半是元素等待不够或者是某个动态内容延迟加载。我的经验是先区分是完全没有这个元素还是元素出现过但被遮挡、不可点击优先用显式等待替代固定的sleep如果元素偶尔可见但点不到试一下driver.tap()配合坐标或者先滚动屏幕让它真正进入可视区域。我曾经遇到一个列表页前几次启动时数据加载快元素很快出现后面因为缓存状态变化加载变慢用例就开始翻车。后来我把所有关键路径都改成显式等待随机失败率从三成降到了接近零。6.3 中文输入乱码怎么回事在Android模拟器上使用send_keys输入中文经常遇到乱码或者压根输入不进去。这个问题我在2.3节提到的两个Capabilities参数里已经埋了伏笔。除了把unicodeKeyboard和resetKeyboard都设为True你还需要确保系统当前有可用的中文输入法。有些精简过的模拟器镜像不带中文输入法那就在设置里加一个谷歌拼音或者搜狗输入法。设置完输入法后重启测试会话再试基本能解决。6.4 定位表达式写了但是报语法错误XPath表达式写错是考试里最常见的低级错误。报错信息里提示invalid xpath时优先检查引号配对和路径首字符。XPath基础规则就三条//表示相对路径查找[属性名值]表示属性筛选text()文本表示文本匹配。括号要注意索引写法是(表达式)[N]而不是表达式[N]这个我前面也强调过新人在这里栽跟头非常多。7. Appium实际落地时的几个心得跑通第一条用例之后很多人会觉得自己入门了但离能稳定落地还有距离。我在团队里推行Appium的过程中总结了几条实际心得。真机还是模拟器别纠结太久。真机更接近用户真实环境但充电、锁屏、网络状态都会影响稳定性模拟器速度快、环境可控适合大批量回归。我的建议是日常开发用模拟器关键版本发布前在真机上跑一遍冒烟测试。另外云测平台也能跑Appium用例适合需要覆盖大量机型矩阵的场景代价是调试时看不到实时日志排错效率低。用例的独立性是所有落地经验里最重要的一条。每个测试方法执行前都要确保App处于预期初始状态不要让上一条用例的状态污染下一条。之前团队有人写了一条创建订单的用例没有清理数据结果后面所有依赖订单列表的用例全部失败。我后来在fixture里加了重置逻辑每个用例跑完自动清数据整个测试集的稳定性一下就上来了。还有一点是日志。别等到报错了才去翻日志要在写用例的时候就养成打印关键步骤的习惯。用Python的logging模块每找到元素、每点击一下都打一条带时间戳的日志。出了问题之后把日志从头翻一遍定位失败的那一步往往一目了然比盯着报错信息瞎猜快得多。我在实际调试中十条问题有九条是靠着完整日志定位出来的。Appium入门这件事说穿了就三步环境跑通、定位熟练、结构清晰。你把这三点啃下来剩下的就交给时间和项目去打磨了。我现在回头看我当时踩过的一个个坑其实都是必经之路只是当时要是有人把这些坑提前跟我讲清楚能少走很多弯路。希望这篇文章能成为你路上的那块垫脚石。
返回列表