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

资讯详情

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

Python+Appium实战:从环境搭建到构建企业级APP自动化框架

Python+Appium实战:从环境搭建到构建企业级APP自动化框架 做企业级 APP 自动化测试时最常见的评估标准不是“脚本能跑”而是“换一台设备能不能跑、换一个版本能不能维护、失败之后能不能快速定位”。Python Appium 是当前测试开发岗位中非常常见的技术组合而拿一款真实电商应用作为练习对象可以同时训练元素定位、用例设计、稳定性处理和报告输出能力。本文以网易严选 APP 为被测对象带你走通“环境搭建 - 会话启动 - Page Object 封装 - 主流程用例 - 失败排查 - 企业级扩展”的完整路径。学完之后你可以把同一套方法迁移到其他 Android 应用上而不只是会操作某一个录制工具。这篇内容按 3 天的节奏编排但更强调的是每一步背后的原因。因为你只有知道为什么这样做遇到 Appium 报错时才不会只能靠重启解决。1. 先理解 Appium 与 APP 自动化的调用链路第一天不建议直接打开安装包开始录制脚本。先花 30 分钟理解链路后面排查问题会轻松很多。1.1 Appium 不是“录制工具”而是 WebDriver 协议的移动端实现很多人误以为 Appium 是一个能自动识别控件并录制的桌面工具。实际上Appium 是一个 Server 程序。当你在 Python 脚本里调用webdriver.Remote()时脚本会向 Appium Server 发起一个 HTTP 请求Appium Server 再把请求转换成 Android 或 iOS 平台能识别的命令。在 Android 平台上常见的自动化驱动是 UiAutomator2。调用大致流程是Python 测试脚本构造 Desired Capabilities并向 Appium Server 请求建立会话。Appium Server 收到会话创建请求后在设备上安装或启动 UiAutomator2 相关服务。脚本继续发送“查找元素”“点击”“输入文本”“滑动页面”等命令。设备端执行命令后把结果返回给 Appium Server再返回给 Python 脚本。这条链路说明了为什么很多报错并不在 Python 代码本身。比如设备没有连接、Appium driver 没有安装、Capabilities 写错、应用没有启动到目标页面都会导致脚本抛异常。1.2 为什么用 Python 写自动化脚本Appium 支持 Java、JavaScript、Python、Ruby、C# 等多种语言。Python 在测试开发领域使用广泛有几点实际原因语法简洁适合把精力放在业务对象和用例逻辑上。有pytest这样成熟的测试框架足以完成用例组织、断言、日志、报告。Appium-Python-Client与 Appium Server 配合成熟API 容易阅读。团队里测试工程师和测试开发通常更容易形成统一的协作语言。需要明确一点语言只是客户端。真正执行控件查找和触摸动作的是设备端的自动化框架Python 负责的是描述“做什么”。1.3 把网易严选当作被测对象时要提前接受哪些变量选择一款真实电商 APP 做练习比使用测试专用 Demo 更接近实际工作。原因是电商 APP 的页面结构非常复杂存在运营位、弹窗、网络图加载、动态列表甚至部分页面是 H5。这些变量会导致自动化用例出现不稳定。例如首页可能弹出隐私政策、优惠券、活动弹窗。搜索结果受运营配置影响。商品价格、销量、推荐位随时可能变化。不同设备分辨率下控件位置不同。不同 Android 版本中页面元素属性可能不同。所以练习的目标不是保证“永远成功”而是设计出一套出现问题时能快速定位、修复后仍可继续使用的框架。后面章节会按照这个思路展开。2. 三天任务切分和前置环境准备环境问题最容易消耗时间。很多入门者第一天就卡在“Appium Inspector 连不上设备”或“session created 失败”。下面直接给出一套可操作的环境搭建顺序。2.1 三天的任务切分以下是建议的 3 天安排。不必完全按小时执行但顺序不要乱。阶段核心任务完成标准第 1 天环境安装、设备连接、Appium 启动网易严选能在 Appium Inspector 中看到网易严选页面结构第 2 天用 Page Object 写出启动 - 搜索 - 查看详情主流程用例能独立运行并通过断言第 3 天接入 pytest、日志、失败截图、Allure 报告补充稳定性处理能输出可查看的 HTML 测试报告并定位出常见问题这套节奏的核心逻辑是“先打开会话再写用例再谈工程化”。如果第 1 天没有打通真实设备上的页面结构第 2 天写元素定位就是在猜没有意义。2.2 需要安装的工具清单不同项目的依赖版本并不完全相同。在安装之前先确认操作系统可以支持以下工具。工具作用验证命令JDKAndroid 工具链和部分自动化组件需要java -versionAndroid SDK Platform-Tools提供adb、uiautomator等命令adb --versionNode.jsAppium Server 是 Node.js 程序node -vAppium Server处理 HTTP 命令并驱动设备appium --versionAppium 的 UiAutomator2 驱动在 Android 设备上执行原生自动化appium driver listPython编写测试脚本python --versionAppium-Python-ClientPython 与 Appium Server 通信的客户端库pip show Appium-Python-Clientpytest用测试框架组织用例pytest --versionAppium Inspector查看页面层级并定位元素桌面应用无命令行验证如果使用的是 Appium 2.x管理驱动的思路和 Appium 1.x 不同。Appium 1.x 默认自带 Android 支持Appium 2.x 需要单独安装 driver。这一点非常重要。2.3 安装顺序和具体命令推荐按“底层工具 - Appium - Python 库”的顺序安装。# 1. 确认底层工具已经可用 java -version node -v python --version # 2. 安装 Appium Server npm install -g appium # 3. 安装 Android UiAutomator2 driver appium driver install uiautomator2 # 4. 安装 Python 依赖 pip install Appium-Python-Client pytest allure-pytest # 5. 启动 Appium Server appium -p 4723如果你所在环境下载较慢可以先检查各工具的安装位置和环境变量。这里不强调太具体的版本号因为企业项目中可能会固定一套兼容版本。落地前应确认Appium Server 版本。UiAutomator2 driver 版本。Appium-Python-Client版本。设备 Android 版本。在 2026 年的技术栈中Appium 2.x 已经成为主流分支新的自动化脚本建议直接基于 W3C Capabilities而不是老旧的 JSONWP 写法。2.4 用 Appium Inspector 验证环境能建立会话Appium Inspector 是定位元素的关键工具。启动 Appium Server 后再打开 Appium Inspector填写 Android 设备信息就能看到设备的页面层级。在 Appium Inspector 的 Remote Settings 中通常需要填写Remote Host127.0.0.1Port4723Path根据 Appium 版本填写。Appium 2.x 下通常连接根路径即可如果使用 Appium 1.x 或旧版客户端可能需要/wd/hub。然后在 Desired Capabilities 区域填写设备信息。这一步先不追求应用启动只验证 UiAutomator2 会话能创建成功。{ platformName: Android, appium:deviceName: Android, appium:automationName: UiAutomator2 }如果点击 Start Session 后能看到设备首页的控件树说明 Appium Server、Node.js、驱动、设备和网络链路已经打通。后面只需要把能力扩展为“启动网易严选”就可以了。2.5 连接真机时最容易漏掉的检查点模拟器和真机的连接方式略有差异。真机连接后建议执行以下命令adb devices -l正常结果会显示emulator-5554 device product:sdk_gphone64 model:sdk_gphone64 或 设备序列号 device usb:xxx transport_id:xxx如果看到unauthorized需要在手机上确认 USB 调试授权。如果看到offline可以尝试重新插拔 USB或者重启 adb 服务adb kill-server adb start-server adb devices这里有一个常见误区Adb 能识别设备不代表 Appium 能创建会话。因为 Appium 还要向设备安装 UiAutomator2 相关的测试服务。如果设备不允许安装应用或者安装失败就会出现会话创建失败。3. 从网易严选拿到包名与 Activity启动脚本不能再靠猜要自动化驱动网易严选必须先知道它的包名和启动 Activity。这两个值不要凭记忆写死应该用自己的设备确认。3.1 安装网易严选并获取包名先把网易严选 APK 安装到设备上。如果已经安装可以通过包管理命令查找真实包名。adb install -r yanxuan.apk adb shell pm list packages | grep -i netease在 Linux 或 macOS 终端中可以用grep过滤。Windows 终端也可以先执行adb shell pm list packages再把输出粘贴到命令里过滤。实际输出的包名可能是package:com.netease.yanxuan网易严选这类渠道应用不同渠道包的后缀可能不同。测试时要确认当前设备安装的是哪个包不要直接复制网上旧文章里的包名。3.2 获取启动 ActivityAndroid 应用可以有一个或多个 Activity。从桌面点击图标时启动的那个 Activity 被称为 launchable activity。可以通过下面的命令获取adb shell cmd package resolve-activity --brief com.netease.yanxuan输出会包含包名和 Activity 路径。例如com.netease.yanxuan/.activity.SplashActivity这里.activity.SplashActivity是相对包名的简写。你拿到的 Activity 可能和我这里的示例不同以自己设备输出为准。还可以用 monkey 命令直接启动应用确认它能正常进入首页adb shell monkey -p com.netease.yanxuan -c android.intent.category.LAUNCHER 1这个命令在调试时很有用。如果应用本身没有安装或者包名错误monkey 会报错。3.3 把 Capabilities 放到 YAML 配置文件中建议把设备信息和应用信息放到配置文件里不要硬编码在 Python 脚本中。这样后续切换测试设备或测试环境时不需要修改代码。创建一个config/config.yamlapp: appPackage: com.netease.yanxuan appActivity: com.netease.yanxuan/.activity.SplashActivity caps: platformName: Android appium:deviceName: Android appium:automationName: UiAutomator2 appium:appPackage: com.netease.yanxuan appium:appActivity: com.netease.yanxuan/.activity.SplashActivity appium:noReset: true appium:unicodeKeyboard: true appium:resetKeyboard: true这里的appActivity只是示例。实际执行前要先把第 3.2 节 resolve-activity 命令得到的值替换进去。3.4 最小 Python 启动脚本在项目根目录创建quick_start.pyimport yaml from appium.webdriver import Remote from appium.options.android import UiAutomator2Options with open(config/config.yaml, encodingutf-8) as f: config yaml.safe_load(f) caps config[caps] options UiAutomator2Options().load_capabilities(caps) driver Remote(command_executorhttp://127.0.0.1:4723, optionsoptions) try: print(当前包名:, driver.current_package) print(当前 Activity:, driver.current_activity) finally: driver.quit()运行python quick_start.py如果输出当前包名: com.netease.yanxuan 当前 Activity: 某个 MainActivity说明 Python、Appium、网易严选三者已经打通。如果这里就报错不要急着写搜索用例先用第 6 章的排查思路处理。3.5 为什么建议在 YAML 中配置 noResetnoReset的含义是“不重置应用状态”。例如测试过程中产生的登录态、本地数据等不会在每次会话开始时被清理。对学习阶段来说设置noReset: true可以减少很多重复登录问题。但在企业级测试环境中是否使用noReset要谨慎如果测试用例依赖干净的应用数据应使用noReset: false或者自己清理数据。如果只想快速看主流程noReset: true能减少变量。注意noReset: true不等于“数据安全”。它只是不让 Appium 在启动时清空数据测试过程中写入的数据仍然会被保留。4. 用 Page Object 把主流程组织成可维护用例第三天前的大部分时间不应该用来把脚本写成“从上到下一长串 find_element”。而是应该按照 Page Object 模式把页面元素和操作行为封装到单独的类中。4.1 为什么不要在用例里写大量 find_element假设你要测试“首页点击搜索框 - 输入关键词 - 点击搜索 - 进入详情页”。如果直接在测试函数里写driver.find_element(...).click() driver.find_element(...).send_keys(保温杯)这个用例能跑通但当页面的某个元素 id 变化时你需要打开测试文件定位到具体某一行去修改。如果用例很多维护成本会迅速上升。Page Object 的核心思想是一个页面对应一个 Page 类。页面上的元素定位信息放在 Page 类中。页面的操作行为封装成方法。测试函数只表达业务意图不关心底层查找细节。例如搜索流程在测试函数里应该长这样def test_search_product(driver): home_page HomePage(driver) home_page.open_search() search_page search_page(driver) search_page.search(保温杯) assert search_page.has_result()后续元素 id 变化只需要去对应的 Page 类中修改测试业务逻辑保持不变。4.2 推荐的项目结构在动手写代码前先建立目录app_auto/ ├── config/ │ └── config.yaml ├── pages/ │ ├── __init__.py │ ├── base_page.py │ ├── home_page.py │ └── search_page.py ├── tests/ │ ├── __init__.py │ └── test_yanxuan_flow.py ├── conftest.py √── requirements.txtpages目录存放 Page Objecttests目录存放测试用例conftest.py管理 pytest 的 fixture。4.3 BasePage 封装等待、点击和输入封装的最基础能力有 4 个查找元素时等待元素出现。点击元素。输入文本。失败时截图。在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 find(self, locator, timeout10): wait WebDriverWait(self.driver, timeout) return wait.until( EC.presence_of_element_located(locator) ) def click(self, locator, timeout10): element self.find(locator, timeout) element.click() def input_text(self, locator, text, timeout10): element self.find(locator, timeout) element.clear() element.send_keys(text) def is_visible(self, locator, timeout5): try: self.find(locator, timeout) return True except Exception: return False def screenshot(self, file_path): self.driver.save_screenshot(file_path)这里的is_visible方法很适合处理弹窗。例如启动后可能出现关闭按钮在不确定是否有弹窗时可以用一个短超时去尝试查找。4.4 首页和搜索页的 Page 类真实网易严选页面的元素 id 需要通过 Appium Inspector 获取。下面代码中的 id 只是示例实际落地前要把每个 id 替换成当前版本的元素属性。在pages/home_page.py中from appium.webdriver.common.appiumby import AppiumBy from pages.base_page import BasePage class HomePage(BasePage): # 以下定位信息仅为示例请通过 Appium Inspector 获取当前元素后替换 search_entry ( AppiumBy.ID, com.netease.yanxuan:id/search_entry ) close_popup ( AppiumBy.ID, com.netease.yanxuan:id/close_popup ) def close_popup_if_exists(self): if self.is_visible(self.close_popup, timeout2): self.click(self.close_popup) def open_search(self): self.click(self.search_entry, timeout15)在pages/search_page.py中from appium.webdriver.common.appiumby import AppiumBy from pages.base_page import BasePage class SearchPage(BasePage): # 以下定位信息仅为示例请通过 Appium Inspector 获取当前元素后替换 keyword_input ( AppiumBy.ID, com.netease.yanxuan:id/keyword_input ) search_button ( AppiumBy.ID, com.netease.yanxuan:id/search_button ) first_product ( AppiumBy.XPATH, //android.widget.TextView[contains(text, 保温杯)] ) def search(self, keyword): self.click(self.keyword_input, timeout15) self.input_text(self.keyword_input, keyword, timeout5) self.click(self.search_button, timeout5) def has_result(self): return self.is_visible(self.first_product, timeout10)这里有一个重要提醒搜索结果的商品标题不一定包含完整关键词也可能是“XXX保温杯 新款”。所以first_product的定位要根据实际搜索结果调整。4.5 编写第一条完整用例在tests/test_yanxuan_flow.py中import pytest from pages.home_page import HomePage from pages.search_page import SearchPage def test_goto_home_and_search(driver): home HomePage(driver) home.close_popup_if_exists() home.open_search() search SearchPage(driver) search.search(保温杯) assert search.has_result(), 搜索结果页没有出现商品通过这段代码你已经从“手动点击”变成了可验证的业务流程。测试中即使元素变化导致失败修复也只需要在 Page 类中定位修改。4.6 页面跳转后要处理活动页和 H5 页网易严选里的部分页面可能不是原生 Android 页面而是 WebView 或内嵌 H5。原生定位器找不到时需要切换上下文。判断方式是在 Appium Inspector 中查看当前页面是否有 WebView 节点。如果有代码中需要contexts driver.contexts # 形如 [NATIVE_APP, WEBVIEW_com.netease.yanxuan]然后切换到对应 WebView 上下文才能使用类似AppiumBy.CSS_SELECTOR的定位方式。这个技术可以等原生主流程稳定后再扩展不要第一天就陷入 WebView 调试。5. 接入 pytest 和 Allure让结果可追溯仅有断言还不够。一个可用的测试工程需要能在执行后告诉团队哪一条用例通过了哪一条失败了失败时页面是什么样子。pytest Allure 可以承担这个职责。5.1 在 conftest.py 中管理 driver 生命周期同一个设备会话可以复用到多条用例但也要控制会话结束。切分测试时通常把 driver 的 scope 设置为module或class避免每跑一条用例就重新启动一次 Appium。在项目根目录创建conftest.pyimport yaml import pytest from appium.webdriver import Remote from appium.options.android import UiAutomator2Options pytest.fixture(scopemodule) def driver(): with open(config/config.yaml, encodingutf-8) as f: config yaml.safe_load(f) caps config[caps] options UiAutomator2Options().load_capabilities(caps) driver Remote(command_executorhttp://127.0.0.1:4723, optionsoptions) yield driver driver.quit()如果在多个模块中共用同一会话需要考虑用例顺序和页面状态。例如A 用例停留在商品详情页B 用例期望从首页开始如果顺序没有控制好B 就可能失败。解决方法是每条用例都从某个确定入口进入例如通过 back 回到首页。或者每条用例都新建会话虽然慢但隔离性好。或者使用 pytest fixture 的function作用域做更细粒度控制。实际项目中我会先保证“单条用例独立可跑”再接入并发和复用。5.2 失败截图和日志页面元素定位失败时截图比日志更有价值。在conftest.py中加入 pytest hookpytest.hookimpl(tryfirstTrue, hookwrapperTrue) def pytest_runtest_makereport(item, call): outcome yield report outcome.get_result() if report.when call and report.failed: driver item.funcargs.get(driver) if driver: screenshot_path freports/screenshots/{item.name}.png driver.save_screenshot(screenshot_path) print(失败截图:, screenshot_path)这样用例失败时会自动把当前设备页面保存为 PNG。再配合日志就能判断是产品缺陷、网络问题还是脚本定位问题。5.3 输出 Allure 报告先安装 Allure 命令行工具。这一步不是 pip 安装的allure-pytest它只是 pytest 和 Allure 之间的适配层。执行用例并生成报告pytest tests -v --alluredir./allure-results allure generate ./allure-results -o ./allure-report --clean allure open ./allure-report生成的 HTML 报告中会包含用例通过率。每个用例耗时。失败时的日志和附件。历史趋势。在企业团队中Allure 报告可以作为自动化执行的“交付物”。5.4 用重复执行检查稳定性一条用例第一次能过不算真正稳定。在接入 CI 前先重复执行多次pip install pytest-repeat pytest tests/test_yanxuan_flow.py -v --count5 --alluredir./allure-results如果连续执行 5 次有 3 次失败说明用例本身存在不稳定因素需要追查页面加载、弹窗、网络或元素等待策略而不是直接交给 CI 每天运行。6. 最容易翻车的问题和排查路径到这里你已经能写出完整脚本。接下来是最重要的部分出现问题后按什么顺序定位。6.1 会话创建失败Appium 无法连接设备现象selenium.common.exceptions.SessionNotCreatedException: Could not create a session: An unknown server-side error occurred while processing the command.排查顺序检查 Appium Server 是否还活着。执行curl http://127.0.0.1:4723/status。检查 Appium 是否安装了对应 driver。执行appium driver list。检查adb devices是否显示device不是unauthorized。检查 Capabilities 是否写了automationName: UiAutomator2。检查设备剩余空间是否能安装 Appium 辅助 APK。查看 Appium Server 日志中的具体报错而不是只看 Python 侧简短异常。6.2 元素定位失败NoSuchElementException现象selenium.common.exceptions.NoSuchElementException: An element could not be located on the page using the given search parameters.可能原因页面还没有加载完成元素尚未出现。定位表达式和当前页面元素不匹配。页面停留在了错误页面。弹窗遮挡了目标元素。目标页面是 WebView但你的定位器是基于 Native 元素。建议检查路径先在 Appium Inspector 中手动打开同一页面确认元素可见。查看测试失败时的截图。把timeout从 5 秒提高到 15 秒试一次。检查是否因为弹窗导致点击被拦截。查看当前 Activity 和预期 Activity 是否一致。不要一开始就增加大量time.sleep。正确做法是先确认页面状态再用显式等待。6.3 中文输入乱码或无法输入现象send_keys输入中文时一部分显示正常一部分变成乱码。输入后键盘没有正常收起。输入框内容与预期不一致。常见原因是 Appium 和手机输入法之间没有正确协调。在使用 Appium 时可以配置appium:unicodeKeyboard: true appium:resetKeyboard: trueunicodeKeyboard表示使用 Appium 自带的无界面输入法来完成 Unicode 输入resetKeyboard表示测试结束后恢复原本输入法。但这两个配置并不是所有场景都能“一劳永逸”。如果还是不行可以尝试不使用send_keys而是通过测试账号数据来绕过复杂文本输入。使用 ADB 方式输入但要注意不同 Android 版本兼容性。先点击输入框等待输入法完全弹出后再输入。6.4 用例能跑但经常失败典型不稳定因素问题表现常见原因处理方向多次运行结果不一致首页弹窗或广告出现时机不确定增加“弹窗关闭”前置处理点击搜索后结果不同运营配置导致搜索结果变化不要断言具体商品名断言结果容器存在滑动后元素位置不对不同分辨率导致坐标变化使用元素定位不要使用固定坐标页面快速跳转时点击无效应用动效导致元素仍处于移动中使用clickable条件等待真机上时好时坏CPU、内存或网络负载影响隔离测试设备关闭高负载后台应用保持用例稳定不是完全消除外部变量而是通过设计来降低敏感性。重要手段包括将“识别弹窗并关闭”封装成前置步骤。断言页面的容器、关键文案而不是断言过于具体的价格。使用WebDriverWait不用固定sleep。为用例设计独立的初始状态。6.5 一个可复用的定位排查清单遇到元素相关问题时建议按下面顺序走一遍元素是否真的在当前页面。当前包名和 Activity 是否符合预期。使用adb shell dumpsys activity top查看当前顶层页面。用 Appium Inspector 获取当前真实控件属性。检查 XPath 是否有严格的层级依赖。检查元素是否被弹窗或浮动按钮遮挡。检查是否存在同 id 的多个元素。检查页面是否为 WebView。这条清单不仅能解决 Appium 问题也能帮助定位应用自身缺陷。7. 距离“企业级”还差哪些能力标题中的“企业级”不是一个营销词。实现一条自动化用例之后还需要继续补上设备管理、测试数据、持续集成和报告归档等能力。7.1 测试账号与测试数据准备在网易严选这类电商应用中很多主流程依赖登录状态。直接使用个人真实账号存在风险也不利于数据复用。建议做法是准备独立的内测账号。把账号信息放到配置中心或环境变量中不要提交到代码仓库。每条用例结束后清理购物车、订单、缓存等数据。如果无法使用真实下单可以通过后端 Mock 或测试桩处理支付环节。自动化测试的价值不只是代替手点而是形成可重复、可追踪的验证闭环。数据管理不到位这个闭环
返回列表