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 -versionjavac -version,能正确显示版本号即成功。

2. 安装Android SDK (或 Android Studio)我们不需要完整的Android Studio IDE,但需要它的SDK工具包,特别是adb(Android调试桥)和uiautomatorviewer(一个用于查看应用元素信息的工具,虽然现在更推荐Appium Desktop里的Inspector)。

  • 操作:下载Android Studio安装包,安装时在“选择组件”页面,确保勾选Android SDKAndroid 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 --versionpython3 --version,显示版本号即成功。

4. 安装网易MuMu模拟器

  • 操作:从网易MuMu官网下载并安装。安装后启动模拟器。
  • 关键设置
    1. 进入模拟器设置,找到“关于平板电脑”或类似选项,连续点击“版本号”7次,开启“开发者选项”。
    2. 返回设置,进入新出现的“开发者选项”,开启“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中)

  1. 启动Appium Desktop并确保Server正在运行(显示绿色的“Stop Server”按钮)。
  2. 点击“Start Inspector Session”按钮(放大镜图标)。
  3. 会弹出一个配置窗口,这里需要填写一个重要的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 // 不重置应用数据 }
  • 如何获取appPackageappActivity
    1. 在MuMu模拟器中打开目标App。
    2. 命令行输入:adb shell dumpsys window | findstr mCurrentFocus(Windows)或adb shell dumpsys window | grep mCurrentFocus(Mac/Linux)。输出结果中/后面的部分就是appPackageappActivity

填写好配置后,点击“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支持多种定位方式,我们需要根据实际情况选择最稳定的一种。

  1. resource-id (推荐首选):相当于Web中的ID,通常最唯一。在Inspector里看到resource-idcom.example.app:id/login_button,那么定位方式就是:

    self.click_element(AppiumBy.ID, "com.example.app:id/login_button")
  2. accessibility-id (content-desc):用于无障碍访问的描述,如果开发有设置,也很稳定。在Inspector里叫content-desc

    self.click_element(AppiumBy.ACCESSIBILITY_ID, "登录按钮")
  3. 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]")
  4. class name:通过控件类型定位,如android.widget.EditText。通常不唯一,需要结合其他条件。

    elements = self.driver.find_elements(AppiumBy.CLASS_NAME, "android.widget.EditText") elements[0].send_keys("username") # 操作第一个输入框
  5. 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 False

2. 处理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主界面),错误信息非常详细。
脚本报错NoSuchElementException1. 元素定位符写错了。
2. 页面尚未加载出来。
3. 元素在WebView或弹窗里,上下文不对。
1. 用Inspector重新确认元素属性。
2. 在查找元素前增加显式等待(WebDriverWait)。
3. 检查当前上下文,如果是混合应用,可能需要切换。
脚本运行缓慢1. 使用了大量的time.sleep()
2. 元素定位策略效率低(如复杂XPath)。
3. 截图、日志操作太频繁。
1. 用显式等待替代固定等待。
2. 优化定位器,优先使用ID、AccessibilityId。
3. 非调试阶段减少不必要的截图。

5.2 脚本稳定性与性能优化心得

  1. 定位器维护:这是长期项目最大的维护成本。和开发团队约定,为关键UI元素添加稳定的resource-idcontent-desc。建立定位器的版本管理意识,UI大改时测试脚本需要同步更新。

  2. 等待的艺术:彻底抛弃time.sleep。混合使用隐式等待(driver.implicitly_wait(10))和显式等待。对于网络加载,可以等待某个“加载完成”的标识元素出现或消失。

  3. 设备与环境的隔离:测试脚本不要依赖特定的设备状态。在setUp方法中,可以尝试关闭应用、清理数据、重启应用,确保每次测试都在一个干净的环境开始。使用"noReset": false可以在会话开始前重置应用数据。

  4. 并发测试考虑:如果你需要同时运行多个测试,确保每个会话使用不同的systemPortudid(对于多开模拟器)。在Desired Capabilities中配置:

    { "systemPort": 8201, // 为每个会话分配不同的端口 "udid": "127.0.0.1:7555" // 指定具体的模拟器地址 }
  5. 使用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模拟器的组合,其灵活性足以应对从简单自动化到复杂企业级测试的需求。记住,自动化测试不是一蹴而就的,从最重要的、最重复的测试用例开始,逐步积累你的页面对象和工具函数,最终你会构建出一个强大的、可维护的自动化测试资产。