尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Python+Appium实战:从环境搭建到网易严选App自动化测试

Python+Appium实战:从环境搭建到网易严选App自动化测试 很多做移动端测试的同学第一次接触 App 自动化时会发现网上资料很多但版本混杂照着旧教程做完Appium Inspector 连不上手机Python 脚本跑起来一堆报错最后连一个真实的业务流程都没跑通。如果你最近正在了解 Python Appium 这套技术栈打算给真实 App 做 UI 自动化测试那么今天这篇文章会比较适合你。文章会用网易严选 App 作为被测对象从环境搭建、核心原理、元素定位到 Page Object 封装、pytest 用例执行和问题排查完整走一遍企业级 APP 自动化测试的落地流程。阅读时可以跟着步骤边配置边验证。掌握这套流程后换到商城、资讯、工具类 App核心思路基本都能迁移使用。1. 背景与核心概念1.1 Appium 到底解决什么问题App 自动化测试的核心痛点是“重复操作太多”。传统手工回归测试里每次版本发布前测试人员都要在手机上反复执行启动、搜索、下单、退出等流程工作量巨大而且容易漏测。Appium 解决的就是这个重复执行问题它允许我们通过 Python、Java、JavaScript 等语言编写脚本像真实用户一样操控 Android 或 iOS 设备上的 App。和很多自动化框架不同Appium 不是针对某一个 App 写死的脚本库而是一个通用测试服务。你可以理解成它给手机提供了一个“遥控中心”测试代码通过 HTTP 请求告诉 Appium启动哪个应用、点击哪个控件、输入什么文字、断言页面是否出现。正因为这种通用性Appium 能适配不同 App企业里也常把它和 pytest、Jenkins 等工具结合形成夜间自动化回归体系。对于 Python 技术栈的测试团队来说Appium 的优势还在于语言生态。Python 能做数据构造、接口调用、测试报告生成再配合 pytest 断言体系很容易把 UI 自动化用例纳入一套完整的质量保障流程中。1.2 Appium 自动化测试的工作方式Appium 不是直接在手机上运行 Python 脚本它底层采用 Client-Server 架构。测试代码里的 Python 是客户端通过 WebDriver 协议把操作命令发送给 Appium ServerAppium Server 再把命令转换成对应移动平台的原生指令。以 Android 为例常见的驱动是 UiAutomator2。Appium Server 收到点击命令后会交给 UiAutomator2 Server 去执行元素查找、点击、输入等操作。iOS 端则使用 XCUITest 驱动完成类似事情。这套设计让测试用例和底层设备实现解耦写脚本时基本不用关心手机系统如何执行点击只需要关心业务元素。这里有个容易混淆的点Appium 2.x 与旧版 Appium 1.x 的驱动安装方式不一样。1.x 时代UiAutomator2 等功能往往内置在 Appium Desktop 安装包里Appium 2.x 之后Server 和 Driver 分离了需要按驱动单独安装。现在学习建议直接使用 Appium 2.x 的稳定版本下面章节也会按照 2.x 的思路来准备环境。1.3 企业级 App 自动化测试的分层思路刚接触自动化时很多人以为“能跑一条脚本”就是自动化完成了。到真实项目里企业级 App 自动化通常不是单条脚本而是一个分层体系分层作用常见技术用例层描述业务流程和断言pytest、unittest页面对象层把页面元素和操作封装成类Page Object 模式驱动与能力层管理设备、Appium Server、Desired CapabilitiesAppium Client基础设施层设备管理、报告、失败重跑、CI 触发pytest-html、Allure、Jenkins本文后面的实战会覆盖用例层、页面对象层和驱动能力层基础设施层会给出建议和扩展方向。这样即使被测 App 变化维护成本也能控制在可接受范围。2. 没有基础如何 3 天上手“3 天搞定企业级 APP 自动化”并不是说 3 天能成为自动化测试专家而是指按合理路线3 天足够从零搭建一套能够跑通真实业务用例的工程。如果之前只会 Python 基础语法没写过移动自动化可以按下面的节奏执行。2.1 Day1跑通环境并启动网易严选 App第一天不追求写复杂逻辑重点是把环境跑通。需要完成 Python 安装、Appium Server 安装、Android 设备连接、网易严选 App 安装最终通过脚本启动网易严选并打印当前页面信息。这一天最容易失败的是环境问题比如 adb 连接不到手机、Appium Inspector 配置错误、Desired Capabilities 不完整。建议遇到一个解决一个并记录到自己的笔记里。环境通了后面学习才会顺畅。2.2 Day2理解元素定位并完成搜索主流程第二天开始理解 UI 自动化怎么找控件。打开 Appium Inspector像查看微信开发者工具一样查看网易严选界面元素弄清 resource-id、text、content-desc、xpath 之间的关系。然后编写搜索流程启动 App、点击搜索框、输入保温杯、点击搜索结果、进入商品详情页。建议不要把目标定得太宽。搜索并进入详情页已经覆盖启动、点击、输入、断言四个核心动作。学会这条链路注册、登录、下单等流程基本就是重复同样套路。2.3 Day3走向工程化第三天做工程化改造把重复代码封装成页面对象类加入显示等待补充失败截图接入 pytest 生成测试报告并处理常见的无法定位、输入不生效问题。3 天结束后应该拥有一个可以直接运行的 Python 自动化项目而不是一堆粘贴在临时 py 文件里的脚本。3. 环境准备3.1 Python、编辑器与依赖如果在 Windows 上学习先从 Python 官网下载稳定版本安装包。安装时务必勾选“Add Python to PATH”否则后面在 cmd 中运行 python 命令会提示找不到。macOS 上可直接通过 Homebrew 安装Linux 则可以按发行版使用包管理器或源码编译。安装完成后在终端里执行python --version pip --version如果两个命令都能正常输出版本号说明 Python 环境没问题。编辑器推荐使用 VS Code 或 PyCharm。VS Code 里需要安装 Python 扩展按 CtrlShiftP 输入 Python: Select Interpreter选择当前 Python 环境PyCharm 则在 Settings 中新建虚拟环境即可。后面创建自动化项目时建议使用 venv 或 conda 虚拟环境避免多个项目依赖互相冲突。本文示例用的是 pip venv 思路。3.2 安装 Appium Server 与 Appium InspectorAppium 2.x 的 Server 基于 Node.js 运行所以电脑上需要先安装 Node.js。安装后打开终端执行npm install -g appium appium --version如果输出了 Appium 版本号说明安装成功。第二步安装 Android 驱动appium driver install uiautomator2执行appium driver list可以查看当前已经安装了哪些驱动。Appium Inspector 是独立的可视化元素定位工具可以把它理解成移动端自动化的“放大镜”通过它查看当前界面的控件树和属性。可以从官方 GitHub Releases 页面下载对应操作系统的安装包安装后打开。启动 Appium Server在终端执行appium如果看到 Appium 服务跑在 4723 端口就说明 Server 已经就绪。Appium Inspector 默认连接地址也是127.0.0.1:4723很少需要修改。3.3 Android 设备准备与获取包名信息Android 自动化测试既可以连接真机也可以使用模拟器。真机测试需要在手机中开启开发者选项和 USB 调试并通过数据线连接电脑。连接后执行adb devices如果设备列表里显示device状态说明 adb 识别正常。接下来需要确认被测应用是否安装。网易严选的包名通常是com.netease.yanxuan不同渠道版本可能不同可以通过如下命令过滤adb shell pm list packages | grep yanxuan拿到包名后再获取当前界面 Activity。先在手机上手动打开网易严选回到终端执行adb shell dumpsys activity activities | grep -E mResumedActivity|mFocusedApp输出会包含类似包名/Activity名的信息。由于 Android 版本不同命令输出格式会有差异但思路是一样的确认包名、确认启动 Activity再把这两个值填到自动化配置里。如果实在无法从命令中解析也可以先随便填写通过 Appium Inspector 连接后查看启动日志日志里通常会显示 Appium 实际识别的 Activity。4. 核心配置与必须掌握的原理4.1 Desired Capabilities告诉 Appium 要操作什么设备Desired Capabilities 是 JSON 格式的配置项用于描述本次自动化会话的属性。它可以告诉 Appium 当前是 Android 还是 iOS、设备名称、应用包名、Activity、是否保留登录状态等。常见的配置如下caps { platformName: Android, appium:automationName: UiAutomator2, appium:deviceName: Android Emulator, appium:platformVersion: 13, appium:appPackage: com.netease.yanxuan, appium:appActivity: MainActivity, appium:noReset: True, appium:unicodeKeyboard: True, appium:resetKeyboard: True, }这里的appium:appPackage和appium:appActivity要与上一步从设备上获取的值保持一致。noReset为 True 时Appium 不会在用例结束后清除 App 数据能避免每次都要重新登录或重新初始化但如果用例之间互相依赖登录状态反而可能掩盖问题实际项目中需要按团队规范决定。4.2 元素定位为什么不能只复制 XPathUI 自动化最关键的操作是“找到按钮并点击”。Appium Inspector 打开网易严选首页后会显示当前界面树。左侧是控件列表右侧是被选元素的属性。定位方式通常有 resource-id、text、content-desc、class name、xpath 等。尽量优先使用 resource-id 或 text 组合。不要看到 xpath 顺手复制因为页面顶部控件层级一变长 xpath 很容易失效。比如一个常见的写法是old_xpath /hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.LinearLayout这种路径一旦页面加了新布局就挂。更建议用相对可靠的 text 或 class 组合(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().text(搜索))或者退而求其次使用尽量短的 xpath//android.widget.TextView[contains(text,加入购物车)]实际定位复杂页面时可以先在 Appium Inspector 中点击需要的控件重点查看元素的resource-id、text、content-desc。当这几个属性都为空时才使用控件层级关系。4.3 显示等待与隐式等待App 页面加载存在网络延迟元素并不是启动后立即可见。如果脚本像人一样不等待直接点击大部分情况会报NoSuchElementException。初学者常见的做法是time.sleep(5)这种固定等待不适合工程化网络慢时 5 秒不够网络快时白白等 5 秒。工程中更推荐使用 Selenium 的WebDriverWait显式等待它会在规定时间内不断轮询直到元素满足条件WebDriverWait(driver, 20).until( EC.presence_of_element_located(locator) )WebDriverWait里还可以配合visibility_of_element_located、element_to_be_clickable等条件使用。后面项目实战代码里会反复见到。4.4 send_keys 输入失效的常见原因在 App 输入框里输入文字时不少同学会遇到两个问题第一是点击了输入框但光标没有出现第二是 Python 脚本里 send_keys 中文手机上显示乱码或者完全不显示。第一个问题常见于键盘没有弹起这时可以在输入前用driver.tap()点一下输入框区域或者使用 UiAutomator2 的focus能力。第二个问题通常和设备默认输入法有关这也是 Capabilities 中配置unicodeKeyboard与resetKeyboard的原因。前者让 Appium 使用 Unicode 输入方式后者在自动化结束后把键盘重置为设备默认输入法。input_el wait.until( EC.visibility_of_element_located(search_input_locator) ) input_el.click() input_el.send_keys(保温杯)如果搜索结果页面没有“搜索”按钮可点击也可以在输入完成后发送 Android 回车键触发搜索driver.press_keycode(66)不同品牌手机输入法对回车键的响应不同但 66 这个按键码在绝大多数原生输入框里都表示 ENTER很多搜索场景都可以先试这一种。5. 网易严选实战从搜索到进入商品详情5.1 被测流程说明本文执行的业务链路是启动网易严选 App → 在首页点击搜索入口 → 输入商品关键词“保温杯” → 触发搜索 → 点击第一个搜索结果 → 等待进入商品详情页并断言“加入购物车”按钮存在。选择这条链路是因为它不涉及登录、支付等强账号体系测试执行更稳定。真实企业中如果要下单、支付往往要另外准备测试账号和预置商品而且会涉及更复杂的账号信息安全边界。示例不点击真实购买按钮只到详情页断言层级既能覆盖核心 UI 自动化操作又能避免测试数据污染线上订单系统。5.2 项目结构我会用 Page Object Model也就是页面对象模式来组织代码。把每个页面的元素定位和操作封装成独立类测试用例只关心业务流程而不是散落的 find_element。项目结构如下yanxuan_appium_demo/ ├── requirements.txt ├── caps.py ├── pages/ │ ├── __init__.py │ ├── base_page.py │ ├── home_page.py │ ├── search_page.py │ └── goods_page.py └── tests/ ├── __init__.py └── test_yx_search.py这样可以做到网易严选首页布局变了只修改home_page.py搜索逻辑变了只修改search_page.py测试用例本身尽量保持稳定。5.3 创建依赖文件与基础配置先在项目根目录创建requirements.txtAppium-Python-Client pytest pytest-html selenium版本不写死的好处是安装时自动找当前兼容版本。如果你希望追求稳定复现也可以把版本固定下来。然后创建虚拟环境并安装依赖python -m venv venv venv/Scripts/activate pip install -r requirements.txtmacOS/Linux 下激活虚拟环境的命令是source venv/bin/activate。安装完成后编写caps.py# 文件路径caps.py def get_caps(): return { platformName: Android, appium:automationName: UiAutomator2, # 真机或模拟器设备名Appium 2.x 中不能为空 appium:deviceName: Android, # 按当前设备实际 Android 版本填写 appium:platformVersion: 13, # 通过 adb shell pm list packages 确认 appium:appPackage: com.netease.yanxuan, # 通过 adb shell dumpsys activity 获取 appium:appActivity: MainActivity, appium:noReset: True, appium:unicodeKeyboard: True, appium:resetKeyboard: True, # 超过 300 秒未收到命令自动关闭会话 appium:newCommandTimeout: 300, }这里要特别强调appPackage与appActivity不要照抄。不同版本、不同渠道的网易严选安装包启动 Activity 可能存在差异。先按 3.3 节的方法从设备拿到正确值再回填到配置中。5.4 封装 BasePageBasePage是所有页面对象的父类统一封装查找、点击、截图、等待逻辑。这样后续写 HomePage、SearchPage 时代码会干净很多。# 文件路径pages/base_page.py from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class BasePage: def __init__(self, driver): self.driver driver def wait_visible(self, locator, timeout15): return WebDriverWait(self.driver, timeout).until( EC.visibility_of_element_located(locator) ) def click(self, locator, timeout15): element WebDriverWait(self.driver, timeout).until( EC.element_to_be_clickable(locator) ) element.click() def send_keys(self, locator, text, timeout15): element self.wait_visible(locator, timeout) element.click() element.send_keys(text) def screenshot(self, file_namescreenshot.png): self.driver.save_screenshot(file_name) def text_exists(self, text, timeout8): try: locator ( AppiumBy.ANDROID_UIAUTOMATOR, fnew UiSelector().textContains({text}), ) WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) return True except Exception: return Falsetext_exists方法用于断言当前页面是否出现指定文本。它使用 UiSelector 的 textContains 匹配适合“加入购物车”这类容易包含在按钮或标题中的文案。5.5 编写首页、搜索页与商品详情页首页页面对象类如下# 文件路径pages/home_page.py from appium.webdriver.common.appiumby import AppiumBy from pages.base_page import BasePage from pages.search_page import SearchPage class HomePage(BasePage): # 网易严选不同版本首页搜索入口文案可能有差异请用 Appium Inspector 确认 SEARCH_ENTRY ( AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().textContains(搜索), ) def open_search(self): self.click(self.SEARCH_ENTRY) return SearchPage(self.driver)如果首页顶部搜索框文案不是“搜索”而是“搜索严选商品”或某个输入框 icon只需要把定位改成实际控件即可。搜索页对象类如下# 文件路径pages/search_page.py from appium.webdriver.common.appiumby import AppiumBy from pages.base_page import BasePage from pages.goods_page import GoodsPage class SearchPage(BasePage): SEARCH_INPUT ( AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().className(android.widget.EditText), ) def search(self, keyword): self.send_keys(self.SEARCH_INPUT, keyword) # 部分页面没有搜索按钮按回车键触发搜索 self.driver.press_keycode(66) def enter_first_result(self, keyword): # 搜索后通常标题里会包含搜索关键词 result_item ( AppiumBy.ANDROID_UIAUTOMATOR, fnew UiSelector().textContains({keyword}), ) self.click(result_item) return GoodsPage(self.driver)enter_first_result默认点击第一个包含关键词的文本控件。如果真实页面的搜索结果被广告或推荐位干扰这里可能需要改成点击某一种商品卡片容器但原理相同先等待条件满足再找到可点击元素。商品详情页对象类如下# 文件路径pages/goods_page.py from appium.webdriver.common.appiumby import AppiumBy from pages.base_page import BasePage class GoodsPage(BasePage): ADD_CART_TEXT 加入购物车 def has_add_cart_button(self): return self.text_exists(self.ADD_CART_TEXT)商品详情页结构复杂这里只做“加入购物车按钮是否存在”的断言。若页面中该按钮是导航栏上的一个 icon而文案出现在别的位置同样可以通过 Inspector 观察后调整。5.6 编写 pytest 测试用例测试用例通过 pytest 管理这里把 driver 初始化放进 fixture保证用例结束后能正确退出会话。# 文件路径tests/test_yx_search.py import pytest from appium import webdriver from caps import get_caps from pages.home_page import HomePage pytest.fixture(scopemodule) def driver(): base_url http://127.0.0.1:4723/wd/hub appium_driver webdriver.Remote(base_url, get_caps()) yield appium_driver appium_driver.quit() def test_search_goods_to_detail(driver): # 启动后进入网易严选首页 home_page HomePage(driver) # 点击搜索入口 search_page home_page.open_search() # 输入关键词并搜索 search_page.search(保温杯) # 进入第一个搜索结果商品详情 goods_page search_page.enter_first_result(保温杯) # 断言详情页出现“加入购物车”按钮 assert goods_page.has_add_cart_button() # 截图作为测试证据 goods_page.screenshot(goods_detail.png)测试用例本身非常接近自然语言。这样做的好处是以后不管底层元素定位怎么变化只要页面对象类保持不变测试人员看用例就能理解业务意图。5.7 运行与结果说明先确保 Appium Server 在运行再进入项目虚拟环境执行pytest tests/test_yx_search.py -s -v --htmlreport.html如果一切正常终端会类似输出PASSED tests/test_yx_search.py::test_search_goods_to_detail同时会在项目根目录生成report.html和goods_detail.png。打开截图可以看到脚本确实操作到了商品详情页。假如测试失败优先检查失败弹窗和页面截图。因为BasePage中用了显示等待很多失败不是“没有元素”而是等待超时。超时原因通常是定位表达式的文案与当前页面不一致或者页面带着开屏广告/弹窗。这时打开 Appium Inspector 人工查看一下当前页面结构很快能定位问题。6. 常见问题与排查思路6.1 问题速查表问题现象常见原因解决思路创建 Session 失败Appium Server 未启动或 Capabilities 格式错误检查 Appium 终端日志确认 4723 端口可访问找不到设备adb 未识别真机或模拟器执行 adb devices 确认设备状态为 device点击搜索入口后无反应首页文本和定位表达式不匹配用 Appium Inspector 重新获取实际文本或 resource-idNoSuchElementException元素尚未加载完成或定位无效使用显式等待替换更合适的选择器send_keys 输入中文乱码输入法不是 Appium Unicode 键盘Capabilities 中增加 unicodeKeyboard 和 resetKeyboard点击元素报 stale element页面刷新后旧元素对象失效重新定位不要让元素对象跨长时间复用跑一次成功后第二次失败noReset 导致页面停留在上一个状态用例前置或后置需要恢复初始页面6.2 遇到 Session 创建失败怎么办Session 创建失败是最早遇到的高频问题。先看 Appium Server 终端日志常见错误是Could not find a connected Android device、Activity used to start app doesnt exist或UiAutomator2 driver is not installed。如果是设备连接问题回到adb devices检查。如果是 Activity 问题回到设备上手动打开网易严选再用dumpsys activity获取正确 Activity。如果是驱动问题执行appium driver install uiautomator2。6.3 元素定位超时怎么办测试失败日志里出现TimeoutException时不要急着改等待时间。先手动执行到失败那一步观察页面真实状态。常见情况是页面弹出了隐私协议弹窗、搜索后出现空结果、不同测试机分辨率导致控件文本重叠。针对弹窗可以在启动流程中增加关闭弹窗逻辑针对空结果需要准备稳定测试关键词或者测试数据。盲目把 timeout 调大只会让用例变慢并不能真正解决元素不存在问题。7. 工程化与最佳实践7.1 坚持 Page Object 模式刚开始学习 UI 自动化时很容易把页面的元素定位直接写在测试用例里比如driver.find_element(...).click()这样写几个用例还能忍受用例一旦超过 20 条页面结构调整一次就会改到崩溃。Page Object 模式的核心是一个页面一个类页面元素和操作都封装在类里。测试用例和页面细节隔离后日常维护工作量会
返回列表