Python+Appium+MuMu模拟器:安卓自动化测试环境搭建与实战

Python+Appium+MuMu模拟器:安卓自动化测试环境搭建与实战
1. 项目概述为什么我们需要告别“点点点”如果你是一名安卓开发者、测试工程师或者正在做一个需要反复操作手机App的期末大作业那你一定对“点点点”深恶痛绝。手动测试一个登录功能你可能需要输入十几种用户名密码组合测试一个商品列表你得不断上拉、下拉、点击不同商品。重复、枯燥、易出错还占用了大量本该用于思考和创新设计的时间。更别提那些需要7x24小时运行的稳定性测试或兼容性测试了人力根本不可能完成。这就是自动化测试的价值所在。它就像给你的测试工作配了一个不知疲倦、绝对精准的“数字员工”。而“Python Appium 网易MuMu模拟器”这套组合正是目前个人开发者、中小团队乃至学生群体入门移动端自动化测试性价比最高、最友好的技术栈。Python语法简洁生态丰富Appium作为行业标准支持原生、混合和Web应用网易MuMu模拟器则提供了一个稳定、免费且高性能的安卓虚拟环境完美解决了“没有那么多真机”的痛点。今天我就以一个过来人的身份带你从零开始手把手搭建这套环境。这不是一篇照搬官方文档的教程而是融合了我自己踩过的无数坑、调试到半夜的经验总结。我们的目标很明确让你在一天之内跑通第一个自动化测试脚本真正把双手从重复劳动中解放出来。2. 环境配置全景图与核心工具选型在动手之前我们先理清整个技术栈的脉络和每个组件的作用。这能帮你理解每一步操作的意义而不是机械地复制命令。2.1 技术栈角色解析整个自动化测试流程可以类比为一场“木偶戏”Python编剧与导演 我们用Python编写测试脚本定义测试流程和逻辑比如先点这里再输入那个。它负责发出所有指令。Appium Server翻译与指挥 Appium是一个HTTP服务器。它接收来自Python脚本通过Client库的指令如“点击坐标(100,200)”并将这些指令“翻译”成安卓系统能理解的UI Automator命令或iOS的XCUITest命令。网易MuMu模拟器舞台与演员 它模拟了一台完整的安卓手机提供了App运行的环境。我们的测试App就安装并运行在其中。Appium Client库 Selenium传令兵 在Python脚本中我们通过appium-python-client这个库来和Appium Server通信。这个库本身基于Selenium WebDriver协议所以如果你有Web自动化经验会发现很多API是相通的。ADB舞台后台通道 Android Debug Bridge是安卓SDK自带的一个多功能命令行工具。它是连接电脑测试脚本和模拟器/真机的桥梁。Appium底层依赖ADB来安装应用、推送文件、获取设备信息、执行Shell命令等。2.2 为什么选择网易MuMu模拟器市面上安卓模拟器很多如雷电、夜神、BlueStacks。我选择MuMu作为教学和推荐环境基于以下几点实战考量对ADB支持友好且稳定 MuMu模拟器默认就开启了ADB调试并且端口固定通常是7555连接非常稳定。有些模拟器需要手动开关ADB或者端口经常变动增加不必要的配置复杂度。性能与兼容性平衡 MuMu在渲染效率和资源占用上表现不错能流畅运行大多数App。其内置的安卓版本常见为7.1或9也能覆盖大部分应用的测试需求。纯净与免费 个人使用免费且广告干扰相对较少不会在测试过程中弹出莫名弹窗影响自动化脚本执行。多开与管理方便 对于需要兼容性测试同时测试多个安卓版本或分辨率的场景MuMu的多开管理器操作直观方便同时控制多个模拟器实例。注意 虽然我们以MuMu为例但本文介绍的环境配置方法尤其是ADB连接、Capability设置同样适用于其他模拟器或真机你只需知道如何找到对应设备的连接信息即可。3. 保姆级环境配置实战接下来我们进入实操环节。请严格按照顺序操作因为很多工具有依赖关系。3.1 第一步安装Java开发工具包JDKAppium Server是基于Node.js的但其底层驱动安卓设备需要用到Android SDK而Android SDK依赖于Java环境。所以JDK是我们的基石。下载 访问Oracle官网或OpenJDK站点如Adoptium。对于新手我建议直接下载JDK 8或JDK 11的LTS版本兼容性最广。下载对应你操作系统Windows/macOS的安装包。安装 运行安装程序记住你的安装路径例如C:\Program Files\Java\jdk-11.0.xx。配置环境变量Windows重点右键“此电脑” - “属性” - “高级系统设置” - “环境变量”。在“系统变量”部分新建一个变量名为JAVA_HOME的变量值为你的JDK安装路径如C:\Program Files\Java\jdk-11.0.xx。找到系统变量Path点击编辑新建一条记录填入%JAVA_HOME%\bin。验证 打开命令行CMD或PowerShell输入java -version和javac -version。如果正确显示版本号说明配置成功。实操心得 环境变量配置失败是新手第一道坎。务必检查JAVA_HOME的路径是否包含到jdk目录本身而不是jre目录。Path中引用的是%JAVA_HOME%\bin这个百分号引用方式很重要。3.2 第二步安装安卓SDK与配置ADB如今Google不再提供独立的SDK安装包通常通过Android Studio来管理SDK。但我们只是为了用ADB等工具可以只安装“Command Line Tools”。下载Android Studio 前往Android开发者官网下载Android Studio安装包并安装。在安装向导中它会询问你是否安装Android Virtual DeviceAVD可以先取消因为我们用MuMu模拟器。打开SDK管理器 启动Android Studio在欢迎界面或项目界面找到“More Actions”或“Configure”打开“SDK Manager”。安装SDK Tools 在“SDK Platforms”标签页选择一个安卓版本如Android 9.0 “Pie”的SDK进行安装。然后切换到“SDK Tools”标签页。勾选关键工具 确保以下工具被勾选并安装Android SDK Build-Tools(选择一个较高版本如30.0.3)Android SDK Platform-Tools(这个最重要包含了adb.exe)Android SDK Tools(旧版可能已整合)Android Emulator(可选但我们用MuMu)记录SDK路径 安装完成后记下你的Android SDK安装路径通常在C:\Users\[你的用户名]\AppData\Local\Android\Sdk或你自定义的位置。配置环境变量新建系统变量ANDROID_HOME值为你的SDK路径如C:\Users\YourName\AppData\Local\Android\Sdk。编辑系统变量Path添加以下两条记录%ANDROID_HOME%\platform-tools(这是adb所在目录)%ANDROID_HOME%\tools(和tools\bin)验证ADB 打开新命令行窗口输入adb version。如果显示版本信息恭喜你最重要的桥梁工具就绪了。3.3 第三步安装Python与必要库安装Python 访问Python官网下载最新稳定版如Python 3.10。安装时务必勾选“Add Python to PATH”这能省去手动配置环境变量的麻烦。验证Python与pip 命令行输入python --version和pip --version确认安装成功。安装核心Python库 在命令行中执行以下命令使用国内镜像源加速下载。pip install appium-python-client selenium -i https://pypi.tuna.tsinghua.edu.cn/simpleappium-python-client Appium的Python客户端库是我们写脚本的主力。selenium WebDriver协议的Python实现是前者的基础依赖。3.4 第四步安装Node.js与Appium ServerAppium Server是一个Node.js应用所以我们需要Node.js环境。下载安装Node.js 访问Node.js官网下载LTS长期支持版安装包。安装过程一路下一步即可它会自动将npmNode.js的包管理器添加到环境变量。验证Node.js与npm 命令行输入node -v和npm -v显示版本号即成功。安装Appium Server 通过npm全局安装Appium。npm install -g appium-g代表全局安装这样你可以在任何路径下启动Appium。安装Appium Doctor强烈推荐 这是一个环境诊断工具能帮你检查环境配置是否完整。npm install -g appium-doctor安装后运行appium-doctor它会逐项检查JDK、ANDROID_HOME、ADB等。如果有任何项目标红✖请根据提示修复。直到所有必要项目都显示绿色✔你的基础环境才算过关。可选安装Appium Desktop 这是带图形界面的Appium Server内置了一个用于定位元素的Inspector工具对新手非常友好。你可以从Appium官网下载安装。但在后续的脚本驱动中我们主要使用命令行启动的Server因为它更轻量、更适合集成到自动化流程。3.5 第五步安装并配置网易MuMu模拟器下载与安装 前往网易MuMu模拟器官网下载最新版本并安装。启动模拟器 安装完成后启动MuMu模拟器等待它完全进入安卓桌面。建议在模拟器设置中将“性能设置”调整为“中等”或“高”取决于你电脑配置并关闭“帧率优化”等可能影响脚本稳定性的选项。获取设备连接信息MuMu模拟器默认使用adb connect 127.0.0.1:7555进行连接。这个端口号7555是MuMu特有的。打开命令行输入adb connect 127.0.0.1:7555。看到connected to 127.0.0.1:7555即表示连接成功。输入adb devices你应该能看到一个设备列表其中包含127.0.0.1:7555 device这一行。这个设备标识符127.0.0.1:7555就是我们后续脚本中需要指定的udid。踩坑记录 有时adb connect会失败提示cannot connect。通常的解决步骤是1. 确保MuMu模拟器已完全启动。2. 在MuMu多开器中对当前模拟器实例点击“ADB调试”确认开启。3. 重启电脑的ADB服务adb kill-server然后adb start-server再重试连接。4. 编写并运行你的第一个自动化测试脚本环境全部就绪现在让我们来点真正的“魔法”。我们将编写一个简单的脚本在MuMu模拟器中打开系统自带的“设置”应用并点击“关于手机”选项。4.1 启动Appium Server首先我们需要启动Appium Server让它作为中间人开始工作。打开一个新的命令行窗口保持MuMu模拟器运行输入appium如果一切正常你会看到大量日志输出最后几行类似[Appium] Welcome to Appium v2.x.x [Appium] Appium REST http interface listener started on 0.0.0.0:4723这表示Appium Server已经在本地4723端口启动并监听请求。这个窗口不能关闭保持运行状态。4.2 准备被测应用信息我们需要知道要测试哪个App以及如何启动它。对于系统应用“设置”包名appPackagecom.android.settings启动活动名appActivity.Settings(对于不同安卓版本可能略有差异通用的是.Settings)如何获取一个App的包名和活动名一个简单的方法是使用ADB命令。确保模拟器已连接然后在命令行输入adb shell dumpsys window | findstr mCurrentFocus在Windows上你会看到类似mCurrentFocusWindow{... com.android.settings/.Settings}的输出其中com.android.settings就是包名.Settings就是活动名。4.3 编写Python测试脚本创建一个新的Python文件例如first_test.py用任何文本编辑器或IDE如VSCode、PyCharm打开输入以下代码。我会逐段解释# 导入必要的库 from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy import time # 定义设备连接能力和App信息 desired_caps { # 必填参数平台名称固定为Android platformName: Android, # 必填参数平台版本打开MuMu模拟器设置-关于手机查看安卓版本号如9.0 platformVersion: 9, # 必填参数设备名称任意字符串用于在日志中标识设备 deviceName: MuMu_Emulator, # 必填参数被测应用的包名 appPackage: com.android.settings, # 必填参数被测应用的启动活动名 appActivity: .Settings, # 可选但重要指定连接的设备UDID防止连接错设备 udid: 127.0.0.1:7555, # 可选设置命令超时时间防止因卡顿导致脚本挂死 newCommandTimeout: 60, # 可选是否在会话开始前不重置应用状态不清数据 noReset: True, # 可选设置Unicode输入法确保可以输入中文 unicodeKeyboard: True, resetKeyboard: True, } # 初始化驱动连接到本地的Appium Server # 注意这里的URL固定为 http://127.0.0.1:4723 driver webdriver.Remote(http://127.0.0.1:4723, desired_caps) # 等待应用完全启动这是一个好习惯 time.sleep(3) try: # 示例1使用文本定位并点击“关于手机” # AppiumBy.ANDROID_UIAUTOMATOR 是安卓原生定位方式new UiSelector().text(关于手机)是UiSelector语法 about_phone driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().text(关于手机)) about_phone.click() print(成功点击‘关于手机’) time.sleep(2) # 等待页面跳转 # 示例2使用资源ID定位如果知道的话更稳定 # 假设“关于手机”页面的返回按钮ID是 android:id/up (系统标准ID) # back_btn driver.find_element(AppiumBy.ID, android:id/up) # back_btn.click() # 示例3使用ACCESSIBILITY_ID定位对应content-desc属性如果有的话 # search_btn driver.find_element(AppiumBy.ACCESSIBILITY_ID, 搜索设置) # search_btn.click() print(基础操作执行完毕) except Exception as e: print(f执行过程中出现错误{e}) finally: # 无论成功与否最后都要关闭会话释放资源 time.sleep(5) # 等待5秒让你看到结果 driver.quit() print(测试结束驱动已关闭。)4.4 运行脚本并观察结果确保MuMu模拟器已开启Appium Server命令行窗口正在运行端口4723。在存放first_test.py的目录下打开命令行运行python first_test.py观察你的MuMu模拟器会自动启动“设置”应用如果没打开的话。稍等片刻你会看到屏幕自动滚动并点击了“关于手机”菜单项。命令行窗口会打印出“成功点击‘关于手机’”和“测试结束”的信息。同时观察运行appium的那个命令行窗口你会看到大量的请求和响应日志这就是脚本与Appium Server再通过ADB与模拟器交互的过程。恭喜你你的第一个安卓App自动化测试脚本成功运行了你已经实现了从“手动点点点”到“自动执行”的飞跃。5. 元素定位进阶与核心API解析脚本跑通了但真正的挑战在于如何稳定地找到你想操作的那个按钮、输入框或列表项。元素定位是UI自动化的核心。5.1 八大元素定位策略详解Appium继承了Selenium的定位方式并扩展了移动端特有的方法。以下是最常用、最稳定的几种按推荐优先级排序ACCESSIBILITY_ID(推荐首选) 对应安卓元素的content-desc属性或iOS的accessibility-id。这是为无障碍功能设计的通常具有较好的唯一性和稳定性。如果开发同学规范地设置了这是最佳选择。element driver.find_element(AppiumBy.ACCESSIBILITY_ID, 搜索按钮)ID(非常推荐) 对应安卓元素的resource-id。在原生开发中如果开发者为控件定义了唯一的ID这是最快速、最稳定的定位方式。element driver.find_element(AppiumBy.ID, com.example.app:id/login_button)ANDROID_UIAUTOMATOR(安卓专属强力工具) 使用安卓原生的UiAutomator框架的语法进行定位功能非常强大。通过文本driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().text(登录))通过类名driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().className(android.widget.Button))组合条件driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, new UiSelector().resourceId(com.example:id/btn).text(确认))CLASS_NAME 通过控件类名定位如android.widget.EditText。但通常一个页面同类控件太多不唯一常与其他条件组合使用。XPATH(谨慎使用) 功能强大但脆弱。一旦UI层级结构稍有变化如中间插入了一个容器XPath就可能失效。仅在其他方法都无法定位时作为最后手段。# 尽量避免绝对路径使用相对路径和属性组合 element driver.find_element(AppiumBy.XPATH, //android.widget.Button[text登录])NAME(已废弃) 对应name属性在Appium早期版本使用现在基本被ACCESSIBILITY_ID取代。核心技巧 如何获取这些定位信息使用Appium Inspector或Android SDK自带的uiautomatorviewer工具。它们可以连接到你的设备/模拟器像“照妖镜”一样展示当前页面的UI层级树和每个元素的属性让你轻松复制resource-id,text,content-desc等信息。5.2 常用操作API速查定位到元素后你可以对它进行各种操作点击element.click()输入文本element.send_keys(Hello Appium)输入前最好先清空element.clear()获取文本text element.text获取属性value element.get_attribute(resource-id)或checked,enabled等判断是否显示/可用element.is_displayed(),element.is_enabled()滑动/滚动 Appium提供了swipe,scroll,drag_and_drop等方法但更推荐使用TouchAction或W3C ActionsAPI进行精细的触摸操作。等待机制至关重要 网络延迟、页面渲染慢都会导致脚本找不到元素而报错。必须使用显式等待。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待最多10秒直到ID为‘submit’的元素可被点击 element WebDriverWait(driver, 10).until( EC.element_to_be_clickable((AppiumBy.ID, com.example:id/submit)) ) element.click()永远不要依赖固定的time.sleep()除非是等待动画等非条件性场景。6. 常见问题排查与实战调试技巧即使环境配置完美写脚本时也一定会遇到各种问题。这里记录了几个最高频的“坑”和解决方法。6.1 连接类问题问题现象可能原因排查步骤与解决方案adb devices列表为空1. 模拟器未开启ADB调试2. ADB端口冲突或未连接3. 电脑存在多个ADB版本冲突1. 确认MuMu模拟器“设置”中ADB调试已开或用多开器开启。2. 执行adb kill-serveradb start-server再adb connect 127.0.0.1:7555。3. 命令行输入where adb确保使用的是%ANDROID_HOME%\platform-tools下的adb。Appium Server启动报错提示端口被占用4723端口被其他进程如上次未退出的Appium占用1. 命令行执行netstat -ano | findstr :4723查找占用进程的PID。2. 在任务管理器中结束该PID对应的进程。3. 或启动Appium时指定其他端口appium -p 4724。脚本报错Unable to create a new remote sessionDesired Capabilities 配置错误或Appium Server与客户端版本不兼容1.逐字检查desired_caps字典键名是否正确如appPackage不是apppackge值是否对应安卓版本号对吗2. 检查udid是否与adb devices列出的设备一致。3. 查看Appium Server日志错误信息通常非常详细会直接告诉你缺少哪个Capability。6.2 脚本运行类问题问题现象可能原因排查步骤与解决方案NoSuchElementException找不到元素1. 定位表达式写错2. 页面尚未加载出来3. 元素在WebView或混合应用中1. 使用Appium Inspector确认元素属性复制粘贴定位表达式。2.添加显式等待不要用sleep。3. 如果是WebView需要切换上下文Contextdriver.switch_to.context(WEBVIEW_com.example)再用Selenium方式定位。元素可以找到但click()不生效1. 元素不可点击enabledfalse2. 元素被遮挡3. 需要的是“轻触”而不是“点击”事件1. 检查元素enabled属性。2. 尝试用TouchAction进行点击TouchAction(driver).tap(element).perform()。3. 尝试先等待元素可点击WebDriverWait...element_to_be_clickable。输入框send_keys()无效1. 焦点不在输入框2. 输入法问题1. 先click()一下输入框再send_keys。2. 在Capabilities中设置unicodeKeyboard: True, resetKeyboard: True使用Appium自带的输入法。脚本在模拟器上运行巨慢模拟器性能开销大或脚本等待策略不佳1. 在MuMu设置中分配更多CPU和内存。2. 减少不必要的sleep改用显式等待。3. 考虑使用driver.implicitly_wait(10)设置一个全局的隐式等待但不如显式等待精确。6.3 高级调试技巧活用Appium Server日志 运行脚本时仔细观察启动Appium Server的那个终端窗口。错误信息、Appium发送给设备的命令、设备的响应都打印在这里。遇到问题第一反应就是看日志。使用driver.page_source 在脚本中插入print(driver.page_source)可以将当前的UI层级结构以XML格式打印出来方便你分析页面布局尤其是在Inspector无法连接的时候。截图保存证据 在关键步骤或失败时截图便于后续分析。driver.save_screenshot(./screenshot/login_page.png)录制功能辅助 对于完全陌生的App可以先用Appium Desktop的“录制”功能手动操作一遍它会自动生成代码片段你可以参考这些代码来理解如何定位和操作。7. 从脚本到框架构建可维护的测试项目单个脚本只能算玩具。要用于实际项目你需要考虑组织架构。下面是一个简单的、可扩展的目录结构建议your_automation_project/ ├── config/ │ ├── __init__.py │ └── desired_caps.py # 存放不同设备的Capabilities配置 ├── page_objects/ # 页面对象模型Page Object Pattern, POP │ ├── __init__.py │ ├── base_page.py # 封装公共方法如查找元素、等待 │ ├── login_page.py # 登录页面所有登录相关元素和操作 │ └── home_page.py # 首页页面 ├── test_cases/ # 测试用例 │ ├── __init__.py │ └── test_login.py # 登录功能的测试用例 ├── utils/ │ ├── __init__.py │ ├── logger.py # 日志记录模块 │ └── common_actions.py # 封装通用操作如滑动、截图 ├── reports/ # 测试报告输出目录 ├── logs/ # 日志文件目录 └── run_tests.py # 测试执行入口文件页面对象模型POP是核心设计模式。它的思想是将每个页面封装成一个类页面的元素定位符是这个类的属性页面的操作如登录、搜索是这个类的方法。这样当UI发生变化时你只需要修改对应页面类中的定位符而不需要修改遍布各处测试用例的代码极大提升了可维护性。例如login_page.py可能长这样from appium.webdriver.common.appiumby import AppiumBy from page_objects.base_page import BasePage class LoginPage(BasePage): # 定位符 USERNAME_INPUT (AppiumBy.ID, com.app:id/et_username) PASSWORD_INPUT (AppiumBy.ID, com.app:id/et_password) LOGIN_BUTTON (AppiumBy.ID, com.app:id/btn_login) ERROR_TOAST (AppiumBy.XPATH, //*[text用户名或密码错误]) def input_username(self, username): self.find_element(*self.USERNAME_INPUT).send_keys(username) return self # 支持链式调用 def input_password(self, password): self.find_element(*self.PASSWORD_INPUT).send_keys(password) return self def click_login(self): self.find_element(*self.LOGIN_BUTTON).click() def login(self, username, password): self.input_username(username).input_password(password).click_login() def is_error_toast_displayed(self): # 检查登录失败的Toast提示是否出现 return self.is_element_present(*self.ERROR_TOAST, timeout5)在测试用例中你的代码将变得非常清晰def test_login_success(self): login_page LoginPage(self.driver) home_page login_page.login(correct_user, correct_pass) assert home_page.is_welcome_message_displayed() def test_login_failed(self): login_page LoginPage(self.driver) login_page.login(wrong_user, wrong_pass) assert login_page.is_error_toast_displayed()走到这一步你已经从一个自动化测试的“玩家”进阶为可以设计小型自动化项目的“工程师”了。记住环境搭建只是起点持续学习元素定位的最佳实践、设计模式、以及如何集成到CI/CD流水线中才是将自动化测试价值最大化的关键。