Android UI自动化测试:uiautomator2与weditor环境搭建与实战指南

Android UI自动化测试:uiautomator2与weditor环境搭建与实战指南 1. 项目概述为什么选择 uiautomator2 weditor 这套组合如果你正在为 Android 应用的 UI 自动化测试寻找一个稳定、高效且易于调试的方案那么uiautomator2和weditor这对黄金搭档绝对值得你花时间研究。我最初接触这套工具是因为厌倦了传统 UI 自动化框架在复杂场景下的不稳定性——要么是元素定位飘忽不定要么是脚本维护成本高得吓人。uiautomator2作为 Google 官方UiAutomator测试框架的 Python 封装它直接与 Android 系统的无障碍服务Accessibility Service和视图服务器View Server通信执行速度和对原生应用的支持度都非常出色。而weditor则是一个基于 Web 的 UI 元素查看和定位工具它解决了自动化测试中最头疼的“元素定位”问题让你能像在浏览器里用开发者工具一样直观地查看 App 的 UI 层级结构并一键生成定位代码。这套组合拳的核心价值在于“所见即所得”的调试体验和**“稳如磐石”的执行能力**。它特别适合测试工程师、开发自测人员甚至是想要批量操作手机完成某些重复任务的个人用户。无论你是想自动化回归测试、兼容性测试还是模拟用户操作进行压力测试从环境搭建到编写第一个脚本整个过程都可以在半小时内跑通。接下来我会带你从零开始手把手搭建这套环境并分享一些我踩过坑后才总结出来的实战技巧。2. 环境搭建前的核心准备与避坑指南在动手安装任何包之前充分的准备工作能避免 80% 的后续问题。很多人一上来就pip install结果遇到各种版本冲突、环境变量缺失白白浪费几个小时。2.1 基础环境检查清单首先确保你的电脑上已经安装了以下基础软件并且版本不要太旧Python 环境uiautomator2是一个 Python 库因此 Python 是必须的。推荐使用 Python 3.7 到 3.10 的版本。Python 3.11 及以上版本可能存在一些第三方依赖的兼容性问题为求稳定建议暂时避开最新版。你可以通过命令行输入python --version或python3 --version来检查。ADB 工具这是与 Android 设备通信的桥梁。uiautomator2底层依赖 ADB 来安装应用、推送文件、执行命令。你需要将 ADB 所在目录添加到系统的环境变量PATH中。验证方法是在命令行输入adb version如果能显示版本号则说明配置正确。一台 Android 设备或模拟器可以是真实的手机建议 Android 5.0 (API 21) 及以上也可以是 Android Studio 自带的模拟器AVD或 Genymotion 等第三方模拟器。强烈建议在初期使用模拟器因为你可以随意折腾不用担心把手机搞乱。注意对于模拟器请确保其运行的是Google APIs或Google Play系统映像而不是单纯的 “AOSP” 映像。因为uiautomator2需要依赖一些 Google 服务来运行测试服务器AOSP 映像可能缺少相关组件。2.2 Python 虚拟环境你的第一个“安全区”这是我最想强调的一点务必使用 Python 虚拟环境。直接在你的系统 Python 或全局 Python 站点包site-packages里安装测试工具是灾难的开始。不同项目、不同工具对库的版本要求可能冲突虚拟环境能为你每个项目创建一个独立的、干净的 Python 运行环境。创建和激活虚拟环境非常简单以项目目录uiautomator2-demo为例# 进入你的项目目录 cd path/to/your/uiautomator2-demo # 使用 venv 创建虚拟环境环境文件夹名为 venv python -m venv venv # 激活虚拟环境 # 在 Windows 上 venv\Scripts\activate # 在 macOS/Linux 上 source venv/bin/activate激活后你的命令行提示符前通常会显示(venv)表示你正在虚拟环境中操作。接下来所有的pip install命令都只会影响这个环境。3. 核心工具安装与初始化配置基础打牢后我们就可以开始安装核心工具了。这个过程分为两步安装 Python 库以及在手机上初始化测试环境。3.1 安装 uiautomator2 与 weditor在激活的虚拟环境中执行以下安装命令。我建议使用国内镜像源来加速下载例如清华源或阿里云源。# 使用清华镜像源安装 uiautomator2 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uiautomator2 # 安装 weditor pip install -i https://pypi.tuna.tsinghua.edu.cn/simple weditor安装完成后可以验证一下python -c import uiautomator2 as u2; print(uiautomator2 安装成功) python -c import weditor; print(weditor 安装成功)3.2 设备连接与初始化这是最关键的一步uiautomator2需要在你的 Android 设备上安装一个守护应用atx-agent和一个测试服务应用com.github.uiautomator。连接设备用 USB 线连接手机并开启手机的“开发者选项”和“USB调试”模式。在命令行输入adb devices你应该能看到你的设备序列号后面跟着device字样。自动化初始化uiautomator2提供了一个非常方便的命令行工具uiautomator2 init来完成所有初始化工作。在命令行执行python -m uiautomator2 init这个命令会自动检测已连接的设备。在设备上安装atx-agent一个常驻后台的守护进程。通过atx-agent安装测试服务 APK (com.github.uiautomator) 和测试应用 APK (com.github.uiautomator.test)。在电脑上安装一个用于截屏和点击的辅助工具minicap和minitouch用于更快的操作。初始化常见问题与解决失败提示Permission denied确保手机屏幕上弹出了“允许USB调试吗”的提示框点击“允许”。部分手机如小米、华为还需要在开发者选项中开启“USB调试安全设置”。一直卡住或下载很慢初始化过程需要从 GitHub 下载资源网络不稳定可能导致失败。你可以尝试使用--mirror参数指定国内镜像python -m uiautomator2 init --mirror https://mirrors.aliyun.com/uiautomator/初始化成功后手机桌面上出现了“ATX”和“UIAutomator”两个应用这是正常的请不要卸载它们。它们是测试的基础服务。3.3 启动 weditor 进行可视化侦查设备初始化成功后我们就可以启动weditor来直观地查看应用界面了。在命令行输入python -m weditor命令执行后它会自动在你的默认浏览器中打开一个本地网页地址通常是http://localhost:17310。这就是weditor的界面。连接设备在 weditor 页面顶部的输入框中输入你的设备序列号就是adb devices列出的那个或者直接输入127.0.0.1:7912这是atx-agent在设备上创建的默认服务地址通过 USB 转发到了本地。然后点击“连接”。刷新界面连接成功后点击右侧的“刷新”按钮或按F5当前手机的屏幕截图和完整的 UI 层级树就会显示在左侧。定位元素你可以点击截图上的任意元素右侧的 UI 树会自动定位到对应的节点并显示该元素的所有属性如resource-id,text,class,bounds等。最棒的是你可以直接点击右侧的“复制”按钮它就会生成对应的uiautomator2定位代码如d(text登录)直接粘贴到你的脚本里就能用。至此你的核心测试环境已经搭建完毕。接下来我们将深入脚本编写和实战。4. 从零编写你的第一个自动化测试脚本环境就绪让我们用一个最简单的例子来感受一下uiautomator2的威力。假设我们要测试手机上的“计算器”应用完成一个“123”的计算。4.1 脚本基础结构创建一个名为test_calculator.py的 Python 文件。import uiautomator2 as u2 import time # 1. 连接设备 # 方式一通过设备序列号连接推荐尤其有多台设备时 d u2.connect(你的设备序列号) # 方式二通过 atx-agent 地址连接USB连接时常用 # d u2.connect(127.0.0.1:7912) # 方式三自动连接当前唯一设备 # d u2.connect() print(f设备信息: {d.info}) # 2. 启动计算器应用 # 我们需要知道计算器的包名和启动 Activity # 一个简单的方法在手机上打开计算器然后在 weditor 里查看顶部的 package 和 activity # 通常系统计算器的包名可能是 com.android.calculator2 或 com.google.android.calculator app_package com.android.calculator2 if d.app_current()[package] ! app_package: d.app_start(app_package) time.sleep(2) # 等待应用完全启动 # 3. 执行计算操作1 2 3 # 假设计算器是标准布局数字和运算符都是可点击的 View d(resourceIdcom.android.calculator2:id/digit_1).click() # 点击数字 1 d(resourceIdcom.android.calculator2:id/op_add).click() # 点击加号 d(resourceIdcom.android.calculator2:id/digit_2).click() # 点击数字 2 d(resourceIdcom.android.calculator2:id/eq).click() # 点击等号 # 4. 验证结果 # 获取结果框的文本。结果框的 resource-id 可能是 com.android.calculator2:id/result result d(resourceIdcom.android.calculator2:id/result).get_text() print(f计算结果: {result}) expected_result 3 if result expected_result: print(测试通过) else: print(f测试失败预期 {expected_result} 实际 {result}) # 5. 结束后可以返回桌面或停止应用 d.press(home) # d.app_stop(app_package)代码解析与技巧连接设备u2.connect()是最核心的入口。使用设备序列号是最稳定的方式。元素定位d(resourceId“id”)是常用的定位方式它通过 Android 控件的唯一资源 ID 来查找精度最高。weditor就是帮你快速获取这个resourceId的神器。操作 API.click()用于点击.get_text()用于获取文本。uiautomator2提供了非常丰富的 API包括滑动 (swipe)、输入 (set_text)、长按 (long_click) 等几乎能模拟所有用户操作。等待与延时在点击操作后使用time.sleep是一种简单的等待但在复杂应用中可能不稳定。更好的方法是使用内置的等待例如d(resourceId“id”).wait(timeout10)等待元素出现或者d.wait_activity(“.MainActivity”, timeout10)等待特定页面出现。4.2 更健壮的脚本使用 Page Object 模式当测试用例增多时把元素定位和操作逻辑混在一起会让代码难以维护。我们可以引入Page Object (PO) 模式将每个页面封装成一个类页面的元素和操作作为类的方法。创建一个pages/calculator_page.py文件class CalculatorPage: def __init__(self, d): self.d d # uiautomator2 的设备对象 # 元素定位器Locators _digit_1 (u2.By.RESOURCE_ID, com.android.calculator2:id/digit_1) _digit_2 (u2.By.RESOURCE_ID, com.android.calculator2:id/digit_2) _op_add (u2.By.RESOURCE_ID, com.android.calculator2:id/op_add) _eq (u2.By.RESOURCE_ID, com.android.calculator2:id/eq) _result (u2.By.RESOURCE_ID, com.android.calculator2:id/result) # 页面操作方法 def click_digit_1(self): self.d(*self._digit_1).click() return self # 支持链式调用 def click_digit_2(self): self.d(*self._digit_2).click() return self def click_add(self): self.d(*self._op_add).click() return self def click_equals(self): self.d(*self._eq).click() return self def get_result(self): # 这里可以增加等待结果稳定的逻辑 return self.d(*self._result).get_text()然后主测试脚本test_calculator_po.py会变得非常清晰import uiautomator2 as u2 from pages.calculator_page import CalculatorPage d u2.connect(你的设备序列号) calc_page CalculatorPage(d) # 启动应用 d.app_start(com.android.calculator2) d.wait_activity(.Calculator, timeout5) # 等待计算器主界面 # 执行操作链式调用让代码更易读 calc_page.click_digit_1().click_add().click_digit_2().click_equals() # 断言结果 result calc_page.get_result() assert result 3, fExpected 3, but got {result} print(Page Object 模式测试通过)使用 PO 模式后如果计算器的界面元素 ID 改变了你只需要在一个地方CalculatorPage类修改定位器所有测试用例都会自动生效大大提升了代码的可维护性。5. 高级技巧与实战问题深度排查掌握了基础之后我们来看看如何应对更复杂的场景和那些让人头疼的“坑”。5.1 处理动态元素与智能等待移动应用很多元素是动态加载的如网络列表、弹窗。使用固定的time.sleep不是好主意要么等不够要么等太久。显式等待元素出现# 等待最多10秒直到“登录”按钮出现 login_btn d(text登录) if login_btn.wait(timeout10): login_btn.click() else: raise TimeoutError(登录按钮未在10秒内出现)等待元素消失常用于等待加载框loading d(classNameandroid.widget.ProgressBar) loading.wait_gone(timeout15) # 等待加载圈圈消失最多等15秒使用exists进行条件判断if d(textContains更新).exists: d(text忽略).click() # 如果有更新弹窗就点忽略5.2 处理弹窗、权限请求和特殊控件监听并处理弹窗uiautomator2提供了watcher机制可以注册一个“监视器”当特定元素出现时自动执行操作。# 定义一个监视器命名为 “ALLOW” d.watcher(ALLOW).when(text允许).click() # 启动监视器它会在后台运行一旦发现“允许”按钮就点击 d.watcher.start() # 在需要的时候也可以手动触发一次检查 d.watcher.run() # 测试结束后记得关闭监视器 d.watcher.remove()这个功能对于处理应用频繁弹出的权限申请、通知弹窗等场景极其有用。操作 WebView对于 App 内的 H5 页面uiautomator2无法直接定位。你需要先切换到 WebView 上下文。在weditor中如果看到WebView组件说明页面包含 H5。在代码中先获取所有可用的上下文Contextcontexts d.app_current() print(contexts) # 查看输出通常包含 ‘NATIVE_APP’ 和 ‘WEBVIEW_包名’切换到 WebView 上下文然后就可以使用 Selenium 的 API 来操作了需要额外安装selenium并下载对应浏览器的驱动如chromedriverd.switch_to.context(WEBVIEW_com.example.app) # 现在 d 变成了一个 WebDriver 对象 d.find_element_by_css_selector(“.login-btn”).click() # 操作完后切回原生上下文 d.switch_to.context(‘NATIVE_APP’)5.3 稳定性提升截图、日志与重试机制一个健壮的测试脚本必须具备排错能力。失败时自动截图这是定位问题最直观的方式。import os from datetime import datetime def take_screenshot(d, name_prefix“error”): timestamp datetime.now().strftime(“%Y%m%d_%H%M%S”) filename f“{name_prefix}_{timestamp}.png” path os.path.join(“screenshots”, filename) d.screenshot(path) print(f“截图已保存: {path}”) return path try: d(text“不存在的按钮”).click() except Exception as e: take_screenshot(d, “element_not_found”) raise e结构化日志记录使用 Python 标准的logging模块替代print。import logging logging.basicConfig(levellogging.INFO, format‘%(asctime)s - %(name)s - %(levelname)s - %(message)s’) logger logging.getLogger(__name__) logger.info(“开始执行计算器测试...”) d.click(0.5, 0.5) logger.debug(“点击了屏幕中心”)操作重试装饰器对于不稳定的操作如网络请求后的元素加载可以设计一个重试机制。import time from functools import wraps def retry(times3, delay1): def decorator(func): wraps(func) def wrapper(*args, **kwargs): for i in range(times): try: return func(*args, **kwargs) except Exception as e: if i times - 1: raise logging.warning(f”{func.__name__} 第{i1}次尝试失败: {e}{delay}秒后重试...”) time.sleep(delay) return wrapper return decorator retry(times3, delay2) def click_login_safely(): d(text“登录”).click()6. 常见问题排查与解决方案实录即使准备得再充分实战中还是会遇到各种问题。下面是我总结的一些高频问题及其解决方法。问题现象可能原因排查步骤与解决方案uiautomator2 init失败提示连接超时或下载失败1. 设备未正确连接或 USB 调试未授权。2. 网络问题无法从 GitHub 下载资源。3. 设备系统缺少必要组件多见于纯净版模拟器。1. 执行adb devices确认设备在线且状态为device。检查手机屏幕是否点击了“允许调试”。2. 使用--mirror参数指定国内镜像源重试。3. 尝试手动安装先adb install一个普通的 APK 看是否成功。对于模拟器更换为Google APIs系统映像。weditor连接成功但刷新后界面空白或提示“无法获取层次结构”1. 设备上的atx-agent或测试服务未正常运行。2. 设备系统版本过高如 Android 10可能存在权限限制。3. 应用本身是游戏或使用了非常规渲染如 Unity、Flutter。1. 在设备上手动打开“ATX”应用查看其运行状态。重启atx-agent:adb shell /data/local/tmp/atx-agent server -d。2. 在开发者选项中尝试开启或关闭“强制使用 GPU 渲染”、“停用 HW 叠加层”等选项。3. 对于游戏或特殊应用uiautomator2可能无法获取标准视图树需考虑其他工具如scrcpy结合图像识别。脚本能运行但点击/输入等操作无效1. 元素定位不准确实际点击位置不对。2. 元素非标准控件无法接收点击事件。3. 屏幕上有遮挡如弹窗。1. 使用weditor的“点击”功能先手动测试定位是否准确。尝试使用bounds坐标定位d.click(x, y)。2. 尝试使用d.send_keys()进行全局输入或使用adb shell input命令模拟。3. 运行脚本前先通过d.watcher处理已知弹窗。脚本在模拟器上运行正常在真机上失败1. 真机性能差异加载慢导致元素未出现。2. 真机厂商定制系统如 MIUI, EMUI有额外的权限或后台限制。3. 真机屏幕分辨率不同。1. 在所有等待操作中增加超时时间。2. 在手机设置中为测试应用ATX、UIAutomator及被测应用开启“自启动”、“后台弹出界面”、“电池优化无限制”等权限。3. 使用相对坐标或百分比坐标而非绝对坐标。确保脚本使用resourceId或text等与分辨率无关的属性定位。如何测试非前台应用后台服务、通知栏uiautomator2主要操作当前前台应用。1.通知栏使用d.open_notification()和d.open_quick_settings()。2.后台应用使用d.app_start(package_name)将其切换到前台。3.全局操作使用d.press(“home”),d.press(“back”),d.press(“recent”)等键码。并行测试多台设备需要管理多个设备会话。为每台设备创建独立的u2.connect()对象并在不同线程或进程中运行测试脚本。注意管理好每台设备的端口转发adb -s 设备号 forward。环境搭建和基础使用只是起点uiautomator2的潜力远不止于此。你可以将它集成到持续集成CI流水线中每天自动执行回归测试可以编写复杂的场景测试覆盖应用的各个角落甚至可以结合图像识别库如opencv-python来处理那些无法通过 UI 树定位的验证码或游戏界面。关键在于从一个小而稳的脚本开始逐步构建起你的自动化测试体系把重复劳动交给机器让自己专注于更有价值的测试设计和问题分析上。