Python+Appium+MuMu模拟器:安卓自动化测试环境搭建与脚本编写实战
1. 项目概述:为什么选择Python+Appium+MuMu模拟器?
如果你正在为移动端应用(尤其是安卓应用)的重复性功能测试、兼容性测试或者数据抓取而头疼,手动点点点不仅效率低下,还容易出错。那么,用代码来模拟人的操作,实现自动化,就是一个必然的选择。在众多自动化测试方案中,Python + Appium + 网易MuMu模拟器的组合,是我个人在多个项目中验证过的高效、稳定且对新手友好的“黄金搭档”。
这个组合的魅力在于它的“各司其职”和“强强联合”。Python作为脚本语言,语法简洁,生态丰富,是自动化逻辑的“大脑”。Appium则是一个开源的、跨平台的移动端自动化测试框架,它遵循WebDriver协议,这意味着你可以用写Web自动化测试的思维来写移动端自动化,学习曲线相对平缓。而网易MuMu模拟器,相比其他安卓模拟器,它在稳定性、兼容性以及对Appium的支持度上表现尤为出色,特别是其内置的ADB调试功能非常方便,能有效避免很多连接上的“玄学”问题。
简单来说,这个项目就是教你搭建一个环境,然后写一段Python代码,让代码控制MuMu模拟器里的App,自动完成登录、滑动、点击、输入等一系列操作。无论是做每日签到脚本,还是做复杂的业务流程回归测试,这套组合拳都能帮你从重复劳动中解放出来。接下来,我会从一个踩过无数坑的实践者角度,带你从零开始,完成整个环境的搭建和第一个脚本的编写。
2. 环境搭建:一步一坑的避雷指南
环境搭建是劝退新手的第一个门槛,网上教程很多,但往往因为系统版本、软件版本更新而失效。我这里会基于当前(可长期稳定的)主流版本,给出详细的步骤和每一个环节的验证方法。我们的目标是:让你的电脑、Python、Appium、MuMu模拟器和手机应用之间,能顺畅地“对话”。
2.1 基础软件安装与配置
这一步是地基,必须打牢。
1. 安装Java Development Kit (JDK)Appium服务器是基于Node.js的,但其底层驱动安卓设备需要JDK中的工具(主要是keytool等)。我们安装JDK 8或JDK 11的LTS版本即可,它们兼容性最好。
- 操作:前往Oracle官网或Adoptium等开源站点下载安装包。安装时注意记住安装路径,例如
C:\Program Files\Java\jdk-11.0.xx。 - 配置环境变量:这是关键步骤。
JAVA_HOME:新建系统变量,值设为你的JDK安装路径(如C:\Program Files\Java\jdk-11.0.xx)。Path:编辑系统变量,新增一项%JAVA_HOME%\bin。
- 验证:打开命令行(CMD或PowerShell),输入
java -version和javac -version,能正确显示版本号即成功。
2. 安装Android SDK (或 Android Studio)我们不需要完整的Android Studio IDE,但需要它的SDK工具包,特别是adb(Android调试桥)和uiautomatorviewer(一个用于查看应用元素信息的工具,虽然现在更推荐Appium Desktop里的Inspector)。
- 操作:下载Android Studio安装包,安装时在“选择组件”页面,确保勾选
Android SDK和Android SDK Platform-Tools。你可以不勾选Android Studio本体,但通常一起安装更方便。 - 配置环境变量:
ANDROID_HOME:新建系统变量,值设为你的SDK安装路径(如C:\Users\你的用户名\AppData\Local\Android\Sdk)。Path:新增%ANDROID_HOME%\platform-tools和%ANDROID_HOME%\tools。
- 验证:命令行输入
adb version,应显示ADB的版本信息。
3. 安装Python这是我们的脚本语言环境。
- 操作:从Python官网下载最新稳定版(如Python 3.10+)。安装时务必勾选
Add Python to PATH,这能省去手动配置环境变量的麻烦。 - 验证:命令行输入
python --version或python3 --version,显示版本号即成功。
4. 安装网易MuMu模拟器
- 操作:从网易MuMu官网下载并安装。安装后启动模拟器。
- 关键设置:
- 进入模拟器设置,找到“关于平板电脑”或类似选项,连续点击“版本号”7次,开启“开发者选项”。
- 返回设置,进入新出现的“开发者选项”,开启“USB调试”功能。MuMu模拟器通常默认已开启并处于可连接状态。
- 验证连接:命令行输入
adb devices。如果看到类似127.0.0.1:7555 device的输出(7555是MuMu默认端口),恭喜你,电脑已经识别到模拟器了。如果没看到,可以尝试在MuMu安装目录的shell文件夹下运行adb_server.exe connect 127.0.0.1:7555进行手动连接。
注意:一个常见的坑是端口冲突。如果你安装了多个安卓模拟器(如MuMu、夜神),它们的ADB端口可能不同,会导致
adb devices列表混乱。此时,可以尝试用adb kill-server然后adb start-server重启ADB服务,再连接指定端口的模拟器。
2.2 Appium生态安装:Server与Client
Appium分为服务器和客户端两部分。服务器负责接收脚本指令并转发给设备;客户端(即我们的Python脚本)负责发送指令。
1. 安装Appium Server有两种主要方式:
- 方式一(推荐新手):Appium Desktop。这是一个图形化界面程序,内置了Appium Server和元素定位工具Inspector。从Appium官网下载安装即可。启动后,直接点击“Start Server”按钮,就能在本地启动一个服务。
- 方式二(适合CI/CD):通过Node.js安装。如果你熟悉命令行,可以安装Node.js后,通过npm安装:
npm install -g appium。安装后,在命令行输入appium即可启动服务。
2. 安装Python的Appium客户端库这就是我们的脚本将要调用的库。在命令行中使用pip安装:
pip install Appium-Python-Client这个库封装了与Appium Server通信的所有协议,让我们能用Python代码轻松地发送“点击”、“滑动”等命令。
2.3 连接测试与元素探测工具
环境搭好了,怎么知道它们能协同工作呢?我们需要一个“侦察兵”——元素定位工具,来查看应用界面的结构。
使用Appium Inspector(在Appium Desktop中)
- 启动Appium Desktop并确保Server正在运行(显示绿色的“Stop Server”按钮)。
- 点击“Start Inspector Session”按钮(放大镜图标)。
- 会弹出一个配置窗口,这里需要填写一个重要的JSON字典,称为“Desired Capabilities”。它告诉Appium Server你要测试什么设备、什么应用。
一个针对MuMu模拟器的基础配置如下:
{ "platformName": "Android", "platformVersion": "10", // 根据你的MuMu模拟器系统版本填写 "deviceName": "MuMu", // 自定义,用于标识 "appPackage": "com.netease.mumu", // 以MuMu模拟器本身为例,实际填你的目标App包名 "appActivity": ".MainActivity", // 目标App的主活动名 "automationName": "UiAutomator2", // 安卓UI自动化引擎,必须 "noReset": true // 不重置应用数据 }- 如何获取
appPackage和appActivity?- 在MuMu模拟器中打开目标App。
- 命令行输入:
adb shell dumpsys window | findstr mCurrentFocus(Windows)或adb shell dumpsys window | grep mCurrentFocus(Mac/Linux)。输出结果中/后面的部分就是appPackage和appActivity。
填写好配置后,点击“Start Session”。如果一切正常,Inspector会连接到模拟器,并显示当前屏幕的截图和完整的UI元素树。你可以点击屏幕上的元素,右侧会显示其属性,如resource-id,text,class,content-desc等。这些属性就是我们后续写脚本时用来定位元素的“坐标”。
实操心得:第一次连接Inspector失败的概率不低。请按顺序排查:① MuMu模拟器是否已启动并开启USB调试?②
adb devices列表里是否有设备?③ Appium Server日志(Appium Desktop主界面)是否有报错?常见错误是automationName未指定或appPackage/Activity错误。多看看日志,它能提供最直接的错误线索。
3. 核心脚本编写:从“Hello World”到实用操作
环境通了,工具会用了,现在让我们开始写代码。我们将从一个最简单的脚本开始,逐步增加复杂度,最终形成一个可用的自动化测试脚本框架。
3.1 初始化驱动与基础操作封装
首先,创建一个Python文件,比如mumu_auto_test.py。
from appium import webdriver from appium.webdriver.common.appiumby import AppiumBy from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time class MuMuAutoTest: def __init__(self): # 1. 定义Desired Capabilities,和Inspector里配置的一致 self.desired_caps = { "platformName": "Android", "platformVersion": "10", # 改为你的模拟器版本 "deviceName": "MuMu", "appPackage": "com.android.settings", # 以系统设置为例,因为它肯定有 "appActivity": ".Settings", "automationName": "UiAutomator2", "noReset": True, "newCommandTimeout": 600, # 命令超时时间设长一些 "udid": "127.0.0.1:7555" # 明确指定MuMu模拟器的地址,避免多设备冲突 } # 2. 连接Appium Server,Server默认运行在本地4723端口 self.driver = webdriver.Remote('http://localhost:4723/wd/hub', self.desired_caps) self.wait = WebDriverWait(self.driver, 10) # 设置一个显式等待,最多等10秒 def quit(self): """退出驱动""" if self.driver: self.driver.quit() # 基础操作封装 def find_element(self, by, value, timeout=10): """查找单个元素,支持显式等待""" try: element = WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located((by, value)) ) return element except Exception as e: print(f"未找到元素: {by}={value}, 错误: {e}") return None def click_element(self, by, value): """点击元素""" element = self.find_element(by, value) if element: element.click() print(f"点击元素: {value}") else: print(f"点击失败,未找到元素: {value}") def input_text(self, by, value, text): """向元素输入文本""" element = self.find_element(by, value) if element: element.clear() # 先清空原有文本 element.send_keys(text) print(f"向元素 {value} 输入文本: {text}") else: print(f"输入失败,未找到元素: {value}") def get_text(self, by, value): """获取元素的文本内容""" element = self.find_element(by, value) if element: return element.text else: print(f"获取文本失败,未找到元素: {value}") return None这段代码定义了一个类,它完成了与Appium Server的连接,并封装了几个最常用的基础操作:查找、点击、输入、获取文本。使用WebDriverWait进行显式等待是最佳实践,它能避免因为网络或应用加载慢导致的“元素找不到”错误,比直接用time.sleep()更智能、更高效。
3.2 元素定位策略详解
脚本如何知道要点哪里?靠的就是元素定位。Appium支持多种定位方式,我们需要根据实际情况选择最稳定的一种。
resource-id (推荐首选):相当于Web中的ID,通常最唯一。在Inspector里看到
resource-id为com.example.app:id/login_button,那么定位方式就是:self.click_element(AppiumBy.ID, "com.example.app:id/login_button")accessibility-id (content-desc):用于无障碍访问的描述,如果开发有设置,也很稳定。在Inspector里叫
content-desc。self.click_element(AppiumBy.ACCESSIBILITY_ID, "登录按钮")XPath (慎用但强大):当元素没有好的ID或描述时使用。可以通过文本、层级关系等定位。但XPath容易因UI微调而失效,稳定性相对较差。
# 通过文本定位 self.click_element(AppiumBy.XPATH, "//android.widget.Button[@text='登录']") # 通过部分文本定位 self.click_element(AppiumBy.XPATH, "//*[contains(@text, '登录')]") # 通过层级关系定位 self.click_element(AppiumBy.XPATH, "//android.widget.LinearLayout[@resource-id='parent']/android.widget.Button[1]")class name:通过控件类型定位,如
android.widget.EditText。通常不唯一,需要结合其他条件。elements = self.driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.EditText") elements[0].send_keys("username") # 操作第一个输入框Android UiAutomator (UIAutomator2引擎专属,强大):这是安卓原生的定位方式,功能非常强大,可以通过复杂的条件组合定位。
# 通过文本匹配 self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("登录")').click() # 通过resource-id匹配 self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().resourceId("com.example.app:id/btn")').click() # 组合条件:类名为Button且文本为登录 self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().className("android.widget.Button").text("登录")').click()
注意事项:元素定位是自动化脚本稳定性的生命线。优先级建议:
resource-id>accessibility-id>Android UIAutomator>XPath。尽量避免使用绝对路径的XPath和依赖索引位置的定位。在写脚本前,多用Inspector观察不同状态(如登录前/后)下元素的属性是否变化。
3.3 编写一个完整的测试用例:以系统设置为例
让我们用上面的类,写一个自动化操作MuMu模拟器“设置”应用的简单例子。这个例子会完成:打开设置 -> 进入“关于平板电脑” -> 查看“版本号”。
def test_settings_operation(self): """测试系统设置应用的基本操作""" print("=== 开始测试系统设置 ===") # 假设我们已经进入了设置主界面 # 1. 滚动查找并点击“系统”或“关于平板电脑”选项(不同系统版本名称可能不同) # 这里使用滚动查找文本的方式,更健壮 system_text = "系统" about_text = "关于平板电脑" # 方法:滚动屏幕直到找到包含特定文本的元素 self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, f'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().textContains("{system_text}"))') self.click_element(AppiumBy.XPATH, f'//*[contains(@text, "{system_text}")]') time.sleep(1) # 等待页面跳转,实际应用中应用显式等待替代 # 2. 在系统菜单中,点击“关于平板电脑” self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, f'new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().textContains("{about_text}"))') self.click_element(AppiumBy.XPATH, f'//*[contains(@text, "{about_text}")]') # 3. 查找并获取“版本号”的文本 version_element = self.find_element(AppiumBy.XPATH, "//*[contains(@text, '版本号') or contains(@text, 'Android 版本')]/following-sibling::android.widget.TextView") if version_element: version = version_element.text print(f"当前系统版本号是: {version}") else: print("未找到版本号信息") # 4. 按返回键,退回上一级(模拟物理按键) self.driver.press_keycode(4) # 4是Android的KEYCODE_BACK time.sleep(0.5) self.driver.press_keycode(4) # 再按一次,退回设置主界面 print("=== 系统设置测试完成 ===") # 在脚本主函数中运行 if __name__ == '__main__': auto_test = MuMuAutoTest() try: auto_test.test_settings_operation() except Exception as e: print(f"测试过程中发生异常: {e}") finally: time.sleep(2) auto_test.quit()这个脚本演示了:滚动查找、通过文本定位、获取元素文本以及模拟物理按键操作。UiScrollable是处理滚动列表的利器。press_keycode(4)是发送返回键指令,其他常用键值还有:3(HOME键)、24(音量+)、25(音量-)等。
4. 高级技巧与实战框架搭建
掌握了基础操作后,我们需要让脚本更健壮、更易维护,能够应对复杂的真实应用场景。
4.1 等待机制:告别time.sleep的笨办法
indiscriminate use oftime.sleep()is the number one cause of flaky (不稳定) tests.
# 不好的做法:固定等待,浪费时间且不可靠 time.sleep(5) element = driver.find_element(...) # 好的做法:显式等待 (Explicit Wait) from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待元素出现(存在于DOM树) element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((AppiumBy.ID, "myElement")) ) # 等待元素可点击 element = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((AppiumBy.ID, "myButton")) ) # 等待元素文本包含特定内容 element = WebDriverWait(driver, 10).until( EC.text_to_be_present_in_element((AppiumBy.ID, "status"), "完成") )你还可以自定义等待条件:
def wait_for_element_text_not_empty(driver, by, value, timeout=10): """自定义等待条件:等待元素的文本不为空""" def _predicate(driver): element = driver.find_element(by, value) return element if element.text.strip() != "" else False return WebDriverWait(driver, timeout).until(_predicate) # 使用 element = wait_for_element_text_not_empty(self.driver, AppiumBy.ID, "loading_indicator")4.2 页面对象模型 (Page Object Model, POM)
这是UI自动化测试中最重要的设计模式。它将每个页面或重要的UI组件封装成一个类,页面的元素定位和操作都放在这个类里。这样,测试脚本(用例)只关心业务流程,不关心具体元素如何定位,大大提升了代码的可读性和可维护性。
以登录页面为例:
# base_page.py - 基础页面类 class BasePage: def __init__(self, driver): self.driver = driver self.wait = WebDriverWait(driver, 10) # login_page.py - 登录页面类 class LoginPage(BasePage): # 元素定位器 (Locators) USERNAME_INPUT = (AppiumBy.ID, "com.app.example:id/et_username") PASSWORD_INPUT = (AppiumBy.ID, "com.app.example:id/et_password") LOGIN_BUTTON = (AppiumBy.ID, "com.app.example:id/btn_login") ERROR_MSG = (AppiumBy.ID, "com.app.example:id/tv_error") # 页面操作方法 def enter_username(self, username): self.wait.until(EC.presence_of_element_located(self.USERNAME_INPUT)).send_keys(username) return self def enter_password(self, password): self.wait.until(EC.presence_of_element_located(self.PASSWORD_INPUT)).send_keys(password) return self def click_login(self): self.wait.until(EC.element_to_be_clickable(self.LOGIN_BUTTON)).click() return self def get_error_message(self): try: return self.wait.until(EC.presence_of_element_located(self.ERROR_MSG)).text except: return None # 在测试脚本中使用 def test_login(): driver = get_driver() # 获取驱动实例的函数 login_page = LoginPage(driver) # 测试用例变得非常清晰 login_page.enter_username("testuser") login_page.enter_password("wrongpass") login_page.click_login() error_msg = login_page.get_error_message() assert "密码错误" in error_msg print("登录失败测试通过")POM模式的好处是,当登录页面的按钮ID从btn_login改成login_btn时,你只需要修改LoginPage类中的一处定位器,所有测试用例都不需要改动。
4.3 处理弹窗、权限请求和混合应用
真实应用中,弹窗和权限请求是绕不开的。
1. 处理系统弹窗(如权限请求)Appium提供切换到上下文(context)的能力,但处理系统弹窗更简单的方法是使用UIAutomator直接定位弹窗上的按钮。
def handle_permission_popup(self, allow=True): """处理常见的权限请求弹窗""" # 尝试查找包含“允许”或“拒绝”的按钮 button_text = "允许" if allow else "拒绝" # 使用UIAutomator查找当前屏幕上所有按钮,并匹配文本 selector = f'new UiSelector().className("android.widget.Button").text("{button_text}")' try: # 设置短时间等待,因为弹窗可能稍后出现 button = WebDriverWait(self.driver, 5).until( lambda d: d.find_element(AppiumBy.ANDROID_UIAUTOMATOR, selector) ) button.click() print(f"已点击权限弹窗的'{button_text}'按钮") return True except: print("未找到权限弹窗或已处理") return False2. 处理WebView(混合应用)如果应用内嵌了H5页面,需要切换到WebView上下文才能操作其中的元素。
# 获取所有可用的上下文 contexts = driver.contexts print(f"可用上下文: {contexts}") # 通常如 ['NATIVE_APP', 'WEBVIEW_com.example.app'] # 切换到WebView上下文 driver.switch_to.context('WEBVIEW_com.example.app') # 此时可以使用Selenium的WebDriver API操作H5元素,如通过CSS选择器 driver.find_element(By.CSS_SELECTOR, ".login-btn").click() # 操作完成后,切回原生上下文 driver.switch_to.context('NATIVE_APP')注意:要操作WebView,必须在Desired Capabilities中开启相关设置:
"chromedriverExecutable": "/path/to/chromedriver"并且确保ChromeDriver版本与模拟器内WebView的Chrome版本匹配。这是一个常见的深坑。
4.4 日志记录、截图与报告生成
一个健壮的测试脚本必须要有完善的日志和证据(截图)记录。
import logging from datetime import datetime class MuMuAutoTestWithLog(MuMuAutoTest): def __init__(self): super().__init__() # 配置日志 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler(f'appium_test_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'), logging.StreamHandler() ]) self.logger = logging.getLogger(__name__) self.screenshot_dir = "screenshots" def take_screenshot(self, name): """截图并保存,以时间戳和名称命名""" if not os.path.exists(self.screenshot_dir): os.makedirs(self.screenshot_dir) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")[:-3] filename = os.path.join(self.screenshot_dir, f"{timestamp}_{name}.png") self.driver.save_screenshot(filename) self.logger.info(f"截图已保存: {filename}") return filename def click_element_with_log(self, by, value): """带日志记录的点击""" self.logger.info(f"尝试点击元素: {by}={value}") if super().click_element(by, value): self.logger.info(f"点击元素成功: {value}") self.take_screenshot(f"after_click_{value.replace(':', '_')}") else: self.logger.error(f"点击元素失败: {value}") self.take_screenshot(f"error_click_{value.replace(':', '_')}")在关键步骤前后调用take_screenshot,在操作时使用click_element_with_log,当测试失败时,你就能清晰地看到日志和问题发生时的屏幕状态,极大方便了调试。
5. 常见问题排查与性能优化
即使按照教程一步步来,你也可能会遇到问题。这里汇总了一些高频问题和解决方案。
5.1 连接与启动问题排查表
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
adb devices列表为空 | 1. MuMu模拟器未启动或未开USB调试。 2. ADB端口冲突或服务异常。 3. 电脑安装了多个ADB版本冲突。 | 1. 确认模拟器已启动,并在设置中开启USB调试。 2. 命令行执行 adb kill-server然后adb start-server,再执行adb connect 127.0.0.1:7555。3. 检查系统Path,确保使用的是Android SDK目录下的adb。在CMD输入 where adb查看。 |
| Appium Server启动失败,端口被占用 | 4723端口被其他程序占用。 | 1. 命令行执行netstat -ano | findstr :4723查找占用进程ID,在任务管理器中结束它。2. 或者在启动Appium时指定其他端口: appium -p 4724。 |
| Appium Inspector连接超时或失败 | 1. Desired Capabilities配置错误。 2. 应用未安装或 appActivity不对。3. 设备未就绪。 | 1. 仔细核对platformVersion,deviceName,appPackage,appActivity,特别是后两者。用adb shell dumpsys window命令确认。2. 查看Appium Server日志(Appium Desktop主界面),错误信息非常详细。 |
脚本报错NoSuchElementException | 1. 元素定位符写错了。 2. 页面尚未加载出来。 3. 元素在WebView或弹窗里,上下文不对。 | 1. 用Inspector重新确认元素属性。 2. 在查找元素前增加显式等待( WebDriverWait)。3. 检查当前上下文,如果是混合应用,可能需要切换。 |
| 脚本运行缓慢 | 1. 使用了大量的time.sleep()。2. 元素定位策略效率低(如复杂XPath)。 3. 截图、日志操作太频繁。 | 1. 用显式等待替代固定等待。 2. 优化定位器,优先使用ID、AccessibilityId。 3. 非调试阶段减少不必要的截图。 |
5.2 脚本稳定性与性能优化心得
定位器维护:这是长期项目最大的维护成本。和开发团队约定,为关键UI元素添加稳定的
resource-id或content-desc。建立定位器的版本管理意识,UI大改时测试脚本需要同步更新。等待的艺术:彻底抛弃
time.sleep。混合使用隐式等待(driver.implicitly_wait(10))和显式等待。对于网络加载,可以等待某个“加载完成”的标识元素出现或消失。设备与环境的隔离:测试脚本不要依赖特定的设备状态。在
setUp方法中,可以尝试关闭应用、清理数据、重启应用,确保每次测试都在一个干净的环境开始。使用"noReset": false可以在会话开始前重置应用数据。并发测试考虑:如果你需要同时运行多个测试,确保每个会话使用不同的
systemPort和udid(对于多开模拟器)。在Desired Capabilities中配置:{ "systemPort": 8201, // 为每个会话分配不同的端口 "udid": "127.0.0.1:7555" // 指定具体的模拟器地址 }使用
appium-uiautomator2-driver的进阶能力:这个驱动支持很多有用的功能,比如直接执行ADB命令、模拟复杂手势(多点触控、长按拖拽)。在脚本中,你可以通过driver.execute_script('mobile: shell', {'command': 'pm list packages'})来执行shell命令,非常强大。
5.3 将脚本集成到CI/CD流水线
自动化测试的最终价值是融入开发流程。你可以使用Jenkins、GitLab CI、GitHub Actions等工具,在代码提交后自动触发测试。
一个简单的GitHub Actions工作流示例(.github/workflows/appium-test.yml):
name: Appium UI Test on: [push] jobs: test: runs-on: windows-latest # 因为MuMu是Windows应用 steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v4 with: python-version: '3.10' - name: Install dependencies run: | pip install -r requirements.txt # 这里可能需要下载并安装MuMu模拟器、Appium等,通常需要自定义Action或提前准备镜像 - name: Start MuMu Simulator and Appium Server run: | # 启动MuMu模拟器的命令(需提前安装并配置好环境变量) start "" "C:\Program Files\MuMu\emulator\nemu\EmulatorShell\NemuPlayer.exe" -m 你的模拟器名称 # 等待模拟器启动 timeout /t 60 /nobreak # 启动Appium Server start /B appium timeout /t 10 /nobreak - name: Run Tests run: | python your_main_test_script.py - name: Upload Screenshots and Logs if: always() # 即使测试失败也上传 uses: actions/upload-artifact@v3 with: name: test-artifacts path: | screenshots/ *.log在CI中运行UI自动化挑战很大,主要在于模拟器/真机的管理和环境准备。可以考虑使用云测平台提供的真机集群,或者专门维护一台用于CI的构建机。
走到这里,你已经掌握了从环境搭建、脚本编写、框架设计到问题排查的完整链条。这套Python+Appium+MuMu模拟器的组合,其灵活性足以应对从简单自动化到复杂企业级测试的需求。记住,自动化测试不是一蹴而就的,从最重要的、最重复的测试用例开始,逐步积累你的页面对象和工具函数,最终你会构建出一个强大的、可维护的自动化测试资产。
