1. 项目概述:为什么选择这个技术栈?
最近在帮团队搭建一套移动端UI自动化测试环境,目标是能快速验证安卓应用的核心业务流程。经过一番调研和踩坑,最终敲定了Python + Appium + 夜神模拟器 + Appium Inspector这套组合拳。这几乎成了国内移动端自动化入门和中小项目验证的“黄金搭档”,原因很简单:免费、易上手、生态成熟、社区活跃。
先说Python,作为胶水语言,其简洁的语法和丰富的库(特别是Appium-Python-Client)让编写测试脚本变得非常高效,你不需要在语言本身上花费太多学习成本。Appium作为核心引擎,它的“一次编写,多端运行”(Write Once, Run Anywhere)理念很吸引人,底层基于WebDriver协议,这意味着如果你有Web自动化的经验,迁移过来会非常顺畅。它不关心你的应用是原生、混合还是纯Web,都能搞定。
那为什么用夜神模拟器而不是真机?对于自动化测试,尤其是开发调试和持续集成初期,模拟器的优势太大了。环境纯净、可快速重置、多开并行、不受硬件设备限制,夜神模拟器在安卓模拟器领域口碑不错,对ADB(Android Debug Bridge)的支持稳定,图形性能也足够应付大多数应用的自动化操作。最关键的是,它能稳定地暴露出Appium所需的Capabilities参数,如deviceName和platformVersion,这对于连接成功至关重要。
最后是Appium Inspector,它可不是简单的元素查看器。你可以把它理解为Appium版的“浏览器开发者工具”。在真机或模拟器上启动待测应用后,Inspector能连接到这个会话,实时抓取UI层级结构,并且能录制你的操作(点击、输入、滑动)并生成对应的代码片段。这对于编写定位脚本来说,效率是几何级提升,避免了反复修改代码、运行脚本的试错循环。
所以,这个部署验证项目的核心目标就明确了:在本地Windows或macOS环境下,成功搭建起这一整套工具链,并完成从环境配置、应用启动、元素定位到执行一个简单自动化脚本的全流程验证。这不仅是技术能力的证明,更是后续开展大规模、可维护自动化测试的基石。
2. 环境准备与核心工具部署
万事开头难,自动化环境搭建的“难”往往就难在依赖项的版本匹配和配置上。下面我会按顺序拆解每个环节,并附上我踩过坑后总结的版本建议。
2.1 Python与基础依赖安装
Python是脚本的运行时环境。我强烈建议使用Python 3.8 到 3.10之间的版本。Python 3.11及以上版本在某些库的兼容性上可能还会遇到问题,而3.7已逐步退出主流支持。安装时务必勾选“Add Python to PATH”,这是后续无数错误的根源。
安装完成后,打开命令行(CMD或PowerShell),验证安装并安装必备的包管理工具:
python --version pip --version接下来,安装核心的Appium客户端库和常用的辅助库:
pip install Appium-Python-Client pip install selenium # Appium依赖WebDriver,通常会自动安装,但显式安装更稳妥 pip install pytest # 推荐使用pytest作为测试框架,比unittest更灵活注意:如果遇到下载慢或超时,可以使用国内镜像源,例如:
pip install Appium-Python-Client -i https://pypi.tuna.tsinghua.edu.cn/simple
2.2 夜神模拟器安装与关键配置
从夜神模拟器官网下载安装包,安装过程无特别之处。安装完成后启动模拟器,你会看到一个完整的安卓桌面。这里有几个必须检查的配置点,直接影响Appium的连接:
- 开启开发者选项与USB调试:这与真机操作类似。进入模拟器的“设置” -> “关于平板电脑” -> 连续点击“版本号”7次,返回上一级就能看到“开发者选项”。进入后,开启“USB调试”。这是Appium通过ADB与模拟器通信的钥匙。
- 记录关键设备信息:在命令行输入
adb devices。如果正常,你会看到类似127.0.0.1:62001 device的输出。这里的127.0.0.1:62001就是你的deviceName(或udid)。夜神模拟器的默认端口是62001,第一个模拟器。如果开了多开,第二个会是62025,以此类推。 - 确认安卓版本:在“设置”->“关于平板电脑”里查看“Android版本”,比如
7.1.2。这个就是你的platformVersion。Appium Server的配置必须与此严格一致。 - 解决常见的ADB冲突:如果你电脑上安装了Android Studio,它自带一个ADB。夜神模拟器在安装目录
\nox\bin下也有自己的ADB。两者可能冲突。一个稳妥的做法是,将夜神bin目录下的adb.exe重命名为nox_adb.exe,并在使用时指定路径,或者将夜神的ADB路径加入到系统环境变量PATH的最前面。
2.3 Appium Server的部署选择
Appium Server是核心服务端,负责接收Python脚本发来的指令,并将其翻译成模拟器或真机可以执行的UI操作。你有两种选择:
- Appium Desktop(推荐给初学者):一个图形化界面程序,内置了Appium Server和Inspector。下载安装后,一键启动服务,非常直观。你可以从Appium官网的Releases页面下载。
- Appium Server via NPM(适合CI/CD或深度定制):通过Node.js的包管理器npm安装。这需要你先安装Node.js。安装命令是
npm install -g appium。启动时通过命令行传参,更灵活,适合集成到自动化流水线中。
对于本次部署验证,我强烈建议使用Appium Desktop。它的优势在于将Server和Inspector集成在一起,并且提供了友好的界面来配置和启动Session。
2.4 Appium Inspector的独立配置
如果你用的是Appium Desktop,Inspector已经内置。但官方也提供了独立的Appium Inspector应用,界面更现代。无论哪种,其工作原理都一样:作为一个客户端,连接到正在运行的Appium Server会话,来检查UI。
这里有一个巨坑需要特别注意:新版本的Appium Inspector(特别是独立版)默认使用了W3C协议,且连接方式有所变化。你不能再像老教程里那样,简单地在Desired Capabilities里填上app路径和设备信息就点“Start Session”了。
正确的连接姿势如下:
- 首先,确保你的Appium Server(无论是Desktop版还是命令行启动的)已经在运行,并监听默认的
4723端口。 - 打开Appium Inspector。
- 在连接配置中,你需要提供一组Desired Capabilities的JSON数据。这个JSON数据必须与你的Python脚本里初始化驱动(Driver)时使用的Capabilities完全一致。
- 更重要的是,你需要在Capabilities里指定
appium:remoteUrl,其值为http://127.0.0.1:4723。这样Inspector才知道去连接哪个Server。
一个用于连接夜神模拟器上“设置”应用的Capabilities配置示例(JSON格式)如下:
{ "platformName": "Android", "appium:platformVersion": "7.1.2", "appium:deviceName": "127.0.0.1:62001", "appium:appPackage": "com.android.settings", "appium:appActivity": ".Settings", "appium:automationName": "UiAutomator2", "appium:noReset": true, "appium:remoteUrl": "http://127.0.0.1:4723" }填好这些,点击“Start Session”,如果一切正常,Inspector窗口就会加载出模拟器上“设置”应用的界面,并显示其UI层级树。
3. 核心连接原理与Desired Capabilities详解
环境装好了,工具齐了,但为什么连不上?十有八九问题出在Desired Capabilities的理解上。这部分是Appium的“灵魂契约”,它告诉Server:“我要以什么样的方式,测试哪个设备上的哪个应用”。
3.1 理解Appium的架构与通信流程
简单来说,整个过程就像一个三层架构:
- 测试脚本层(Client):你用Python写的代码,调用
webdriver.Remote方法,向Appium Server发送HTTP请求(基于JSON Wire Protocol / W3C WebDriver协议)。 - 服务中间层(Server):Appium Server(运行在
4723端口)接收请求,解析Capabilities,确定目标设备和自动化引擎(如UiAutomator2 for Android)。 - 设备执行层(Agent):Appium Server通过ADB与设备通信,并在设备上安装一个测试辅助应用(如
io.appium.uiautomator2.server),由这个应用来最终执行点击、滑动等操作,并返回UI元素信息。
你的Python脚本和Appium Inspector都是“Client”,它们必须和同一个Server用同样的“暗号”(Capabilities)通信,才能操作同一个设备会话。
3.2 关键Capabilities参数解析
下面针对夜神模拟器,详细解释每个关键参数:
platformName:固定为"Android"。告诉Appium目标是安卓平台。appium:platformVersion:必须与夜神模拟器设置的安卓版本完全一致。在模拟器“设置”中查看。填错会导致Server找不到匹配的驱动。appium:deviceName:填写adb devices命令列出的设备标识。对于夜神,通常是127.0.0.1:62001。这个参数主要用于日志记录和区分多设备,在安卓上并非绝对唯一标识,但必须提供。appium:automationName:推荐且默认使用"UiAutomator2"。这是谷歌官方推荐的安卓UI自动化框架,比老旧的UiAutomator1更强大稳定。除非有特殊兼容性问题,否则不要改。appium:appPackage和appium:appActivity: 这是启动一个已安装应用的关键。appPackage是应用包名(如微信是com.tencent.mm),appActivity是入口活动名。如何获取?有一个简单命令:adb shell dumpsys window | findstr mCurrentFocus(Windows)或grep mCurrentFocus(macOS/Linux)。在模拟器打开目标应用后执行,输出结果中/前面的就是appPackage,后面的就是appActivity。appium:noReset: 设为true可以避免会话结束后清空应用数据(如登录状态),提高调试效率。设为false则每次都会重置应用。appium:fullReset: 通常设为false。如果为true,会在会话开始前卸载重装应用,非常耗时。appium:newCommandTimeout: 命令超时时间,单位秒,例如60。如果Appium Server在60秒内没收到新指令,就会自动结束会话。调试时可以设长一点。appium:udid: 设备唯一标识符。对于模拟器,如果你指定了deviceName为ADB看到的地址,这个可以不填。对于真机,这个就是设备的序列号,比deviceName更可靠。
3.3 编写你的第一个连接验证脚本
理论说再多不如一行代码。下面是一个最简化的Python脚本,用于验证整个环境是否联通。这个脚本会打开夜神模拟器上的“设置”应用,然后等待几秒后退出。
from appium import webdriver from appium.options.android import UiAutomator2Options import time # 1. 定义Capabilities,使用UiAutomator2Options更现代 capabilities = UiAutomator2Options() capabilities.platform_name = 'Android' capabilities.platform_version = '7.1.2' # 请改为你的模拟器版本 capabilities.device_name = '127.0.0.1:62001' # 请改为你的设备名 capabilities.automation_name = 'UiAutomator2' capabilities.app_package = 'com.android.settings' capabilities.app_activity = '.Settings' capabilities.no_reset = True capabilities.new_command_timeout = 60 # 2. 指定Appium Server的地址 appium_server_url = 'http://127.0.0.1:4723' # 3. 创建驱动(Driver)对象,建立连接 driver = webdriver.Remote(command_executor=appium_server_url, options=capabilities) # 4. 一个简单的等待,让你能看到应用被打开了 print("连接成功!‘设置’应用已启动。") time.sleep(5) # 等待5秒 # 5. 关闭会话 driver.quit() print("会话结束。")执行这个脚本前的检查清单:
- 夜神模拟器是否已启动并进入主界面?
- Appium Desktop(或通过命令行启动的Appium Server)是否已点击“Start Server”并运行?
- 脚本中的
platform_version和device_name是否修改正确?
如果运行后没有报错,且模拟器上的“设置”应用被自动打开,恭喜你,最艰难的一步——环境联通——已经完成了。
4. 使用Appium Inspector进行元素定位与录制
环境通了,脚本能跑起来了,接下来就是自动化测试的实质工作:操作界面元素。Appium Inspector在这里扮演了“眼睛”和“向导”的角色。
4.1 启动并连接Inspector会话
- 确保你的Appium Server正在运行。
- 打开Appium Inspector。
- 将前面3.3章节中
capabilities字典里的内容(注意,是字典格式,不是UiAutomator2Options对象),原封不动地复制到Inspector的“Desired Capabilities”JSON配置框中。务必包含"appium:remoteUrl": "http://127.0.0.1:4723"。 - 点击“Start Session”。
如果成功,你会看到两个面板:左侧是模拟器的实时截图,右侧是UI的层级结构树(类似于HTML的DOM树)。你可以在左侧截图点击任何元素,右侧树状图会自动定位到对应的节点,并显示该元素的详细信息。
4.2 解读元素属性与定位策略
在右侧选中一个元素后,下方会显示其属性,这些属性就是我们编写定位代码的依据。常见的有:
resource-id: 类似于Web中的id,是最理想的定位方式。在安卓中,它可能看起来像com.android.settings:id/search_action_bar。text: 元素显示的文本内容。content-desc: 内容描述,类似于alt文本,但很多应用开发不规范,可能为空。class: 元素的类名,如android.widget.TextView。xpath: 一个强大的定位语言,可以通过层级关系定位。Inspector可以直接帮你生成XPath,但自动生成的往往很长且脆弱。
定位策略(By): 在Python代码中,我们使用driver.find_element()方法,并传入定位器和定位值。对应关系如下:
By.ID-> 使用resource-id(注意要去掉包名前缀)。例如,定位com.android.settings:id/title,代码应为:driver.find_element(By.ID, “title”)By.XPATH-> 使用XPath表达式。By.ACCESSIBILITY_ID-> 使用content-desc。By.CLASS_NAME-> 使用class。By.ANDROID_UIAUTOMATOR-> 使用UiAutomator2的定位语法,功能强大,可以组合多个条件。
实操心得:优先使用
resource-id,因为它通常是唯一的且最稳定。如果没有,再考虑组合其他属性使用XPath。尽量避免使用绝对坐标或仅靠text定位,因为文本可能变化,且不同语言环境下会失效。
4.3 录制操作与生成代码
Inspector的“录制”功能是学习定位和快速生成脚本原型的利器。
- 在Inspector中点击红色的“录制”按钮。
- 在左侧截图上,执行你想要自动化的操作序列,例如:点击“网络和互联网” -> 点击“WLAN”。
- 你的每一步操作都会被Inspector记录下来,并在右侧面板生成一个操作列表。
- 最关键的一步:点击右上角的“复制代码”按钮。你可以选择Python语言,它会生成类似下面的代码片段:
el1 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="网络和互联网") el1.click() el2 = driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value="WLAN") el2.click() - 将这些代码复制到你的Python脚本中,替换掉之前的
time.sleep,你就得到了一个可以自动执行该操作的脚本。
注意事项:自动生成的代码使用的定位器可能不是最优的(比如过度依赖ACCESSIBILITY_ID)。你应该结合Inspector查看的元素属性,将其优化为更稳定的定位方式,例如改用By.ID。
5. 编写健壮的自动化测试脚本
有了定位元素的能力,我们就可以组装一个完整的、有一定健壮性的测试用例了。让我们以“在设置中打开WLAN开关”为例。
5.1 脚本结构设计与最佳实践
一个良好的测试脚本应该包含以下几个部分:
- 初始化 (Setup):配置Capabilities,初始化驱动。
- 测试步骤 (Test Steps):用清晰的逻辑和注释,描述操作流程。
- 断言/验证 (Assertion):检查操作结果是否符合预期。
- 清理 (Teardown):关闭驱动,释放资源。即使测试失败也要执行清理。
我们使用pytest框架来组织,它能更好地管理用例和生成报告。
首先,安装pytest并创建一个测试文件,比如test_wifi.py。
import pytest from appium import webdriver from appium.options.android import UiAutomator2Options from appium.webdriver.common.appiumby import AppiumBy from selenium.common.exceptions import NoSuchElementException, TimeoutException from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class TestSettingsWifi: """测试设置中的WLAN功能""" @pytest.fixture(scope="class") def driver(self): """初始化Appium驱动,作为测试类的fixture""" capabilities = UiAutomator2Options() capabilities.platform_name = 'Android' capabilities.platform_version = '7.1.2' capabilities.device_name = '127.0.0.1:62001' capabilities.automation_name = 'UiAutomator2' capabilities.app_package = 'com.android.settings' capabilities.app_activity = '.Settings' capabilities.no_reset = True capabilities.new_command_timeout = 120 # 调试时延长超时 appium_server_url = 'http://127.0.0.1:4723' # 创建驱动实例 driver_instance = webdriver.Remote(command_executor=appium_server_url, options=capabilities) yield driver_instance # 将驱动实例提供给测试用例使用 # 所有用例执行完毕后,执行清理 driver_instance.quit() def test_toggle_wifi(self, driver): """测试打开WLAN开关""" # 步骤1:定位并点击“网络和互联网”条目 # 使用显式等待,增加脚本健壮性 try: network_item = WebDriverWait(driver, 10).until( EC.presence_of_element_located((AppiumBy.ACCESSIBILITY_ID, "网络和互联网")) ) network_item.click() print("已进入‘网络和互联网’页面") except TimeoutException: pytest.fail("未能在10秒内找到‘网络和互联网’入口") # 步骤2:定位并点击“WLAN”条目 try: wifi_item = WebDriverWait(driver, 10).until( EC.presence_of_element_located((AppiumBy.XPATH, "//*[@text='WLAN']")) ) wifi_item.click() print("已进入‘WLAN’页面") except TimeoutException: pytest.fail("未能在10秒内找到‘WLAN’入口") # 步骤3:定位WLAN开关(通常是一个Switch组件) # 这里假设开关可以通过“WLAN”这个文本的兄弟节点或特定resource-id定位 # 实际情况需要你用Inspector仔细查看。这里用XPath示例。 try: # 这是一个示例XPath,意为:查找当前页面中,class是Switch且可点击的元素 # 你需要根据Inspector的实际结构调整这个XPath wifi_switch = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((AppiumBy.XPATH, "//android.widget.Switch")) ) # 步骤4:获取开关当前状态,然后进行切换 current_state = wifi_switch.get_attribute("checked") # 返回可能是'true'或'false' print(f"WLAN开关当前状态: {current_state}") wifi_switch.click() # 点击切换状态 print("已点击WLAN开关") # 简单验证:可以再次获取状态,确认已改变(这里作为演示,不严格断言) # WebDriverWait(driver, 5).until( # lambda d: wifi_switch.get_attribute("checked") != current_state # ) # new_state = wifi_switch.get_attribute("checked") # assert new_state != current_state, "WLAN开关状态未发生改变" except (NoSuchElementException, TimeoutException) as e: pytest.fail(f"定位或操作WLAN开关时失败: {e}") except Exception as e: pytest.fail(f"测试过程中发生未知错误: {e}") # 步骤5:返回上一级(可选) driver.back() driver.back() print("测试流程执行完毕。")5.2 显式等待与隐式等待的运用
上面的代码中使用了WebDriverWait,这是显式等待。它针对某个特定条件(如元素出现、可点击)进行等待,最多等10秒,条件满足就立即继续,效率高。这是推荐的最佳实践。
与之相对的是隐式等待driver.implicitly_wait(10),它会在你每次查找元素时,让Driver在全局等待最多10秒。这可能导致脚本整体执行时间变长,并且可能掩盖一些定位逻辑问题。建议不要混用两种等待,优先使用显式等待。
5.3 常用操作API封装示例
除了click(),Appium还提供了丰富的操作。我们可以将这些常用操作封装成函数,提高代码复用性。
from appium.webdriver.common.touch_action import TouchAction class AppiumActions: def __init__(self, driver): self.driver = driver def swipe_up(self, duration_ms=1000): """向上滑动屏幕""" size = self.driver.get_window_size() start_x = size['width'] * 0.5 start_y = size['height'] * 0.8 end_x = size['width'] * 0.5 end_y = size['height'] * 0.2 action = TouchAction(self.driver) action.press(x=start_x, y=start_y).wait(duration_ms).move_to(x=end_x, y=end_y).release().perform() def input_text_safely(self, locator, text, clear_first=True): """安全地输入文本,先定位元素,再清空(可选),最后输入""" element = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located(locator) ) if clear_first: element.clear() # 清空原有文本 element.send_keys(text) def is_element_present(self, locator, timeout=5): """检查元素是否存在(不抛出异常)""" try: WebDriverWait(self.driver, timeout).until( EC.presence_of_element_located(locator) ) return True except TimeoutException: return False # 在测试用例中使用 def test_some_feature(driver): actions = AppiumActions(driver) if not actions.is_element_present((AppiumBy.ID, "target_element")): actions.swipe_up() actions.input_text_safely((AppiumBy.ID, "input_box"), "Hello Appium")6. 常见问题排查与实战技巧
即使按照步骤操作,也难免会遇到问题。下面是我在实战中总结的“排错指南”。
6.1 连接类问题与解决方案
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
adb devices列表为空 | 1. 模拟器未启动。 2. ADB服务未启动或冲突。 3. 模拟器的USB调试未开启。 | 1. 启动夜神模拟器,等待完全进入桌面。 2. 命令行执行 adb kill-server然后adb start-server。3. 检查模拟器“开发者选项”中的“USB调试”是否开启。 |
| Appium Server 启动失败,端口被占用 | 4723端口被其他进程占用。 | 1. 命令行执行netstat -ano | findstr :4723查找占用进程的PID。2. 在任务管理器中结束该进程,或使用命令 taskkill /PID <PID> /F。3. 或者在启动Appium Desktop时指定其他端口,如 --port 4724。 |
Python脚本报错WebDriverException: Unable to create new remote session | 1. Appium Server未运行。 2. Capabilities配置错误(版本、设备名、包名等)。 3. 请求的自动化引擎未安装。 | 1. 确认Appium Server已成功启动并显示监听端口。 2.逐字核对Capabilities,特别是 platformVersion和deviceName。3. 查看Appium Server日志,通常会有更详细的错误信息,例如“Cannot find…”。 |
| Inspector 无法连接,提示超时或会话创建失败 | 1.remoteUrl配置错误。2. Capabilities与Server端不匹配。 3. 使用了过时的Capabilities格式。 | 1. 确认appium:remoteUrl是http://127.0.0.1:4723。2. 确保Inspector和Python脚本使用完全相同的Capabilities( appPackage,appActivity等)。3. 新版本Inspector要求Capabilities使用W3C格式,确保键名带 appium:前缀或使用options对象。 |
6.2 元素定位与操作类问题
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
NoSuchElementException | 1. 定位器写错了。 2. 元素尚未加载出来。 3. 元素在 WebView或Flutter等混合环境中。 | 1. 用Inspector重新确认元素属性,特别是resource-id是否带包名。2.添加显式等待,等待元素出现、可见或可点击。 3. 使用 driver.contexts查看当前上下文,可能需要切换到WEBVIEW_或FLUTTER上下文。 |
| 脚本执行速度慢,或操作不生效 | 1. 使用了隐式等待且时间设置过长。 2. 连续操作间缺少必要的等待。 3. 元素非真正可交互(如被遮挡)。 | 1. 改用显式等待,针对具体操作设置等待条件。 2. 在关键页面跳转后,添加一个短暂的静态等待( time.sleep(1))或等待某个标志性元素出现。3. 使用 element_to_be_clickable条件,确保元素可操作。 |
| 屏幕滑动(Swipe/Scroll)不准确 | 起始/结束坐标计算有误,或设备屏幕尺寸不同。 | 使用相对坐标而非绝对坐标。例如,swipe_up函数中使用屏幕宽高的百分比来计算坐标点,这样能适配不同分辨率的设备。 |
6.3 性能与稳定性优化建议
- 使用
noReset: true:在调试阶段,避免每次会话都重置应用,可以节省大量安装和登录时间。 - 复用Driver会话:对于一组相关的测试用例,使用
pytest的fixture并设置scope="class"或scope="module",让一个Driver服务多个测试,而不是每个用例都重启。 - 善用Page Object模式:这是UI自动化测试的经典设计模式。将每个页面(或主要组件)封装成一个类,页面的元素定位符和基本操作作为这个类的方法。这样能使测试脚本更清晰、更易维护,元素定位改变时只需修改一个地方。
class SettingsMainPage: def __init__(self, driver): self.driver = driver self.network_item = (AppiumBy.ACCESSIBILITY_ID, "网络和互联网") def go_to_network(self): WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable(self.network_item) ).click() return NetworkPage(self.driver) # 返回下一个页面对象 # 在测试用例中 def test_flow(driver): main_page = SettingsMainPage(driver) network_page = main_page.go_to_network() # ... 继续操作 - 日志与截图:在测试关键步骤和失败时,保存截图和日志,便于后期分析。
def take_screenshot(driver, name): timestamp = time.strftime("%Y%m%d_%H%M%S") filename = f"screenshot_failure_{name}_{timestamp}.png" driver.save_screenshot(filename) print(f"截图已保存: {filename}") return filename # 在断言失败或异常捕获处调用 try: # 某些操作 assert something == expected except AssertionError: take_screenshot(driver, "assertion_failed") raise
部署验证成功只是第一步,这套环境的价值在于持续、稳定地服务于你的测试需求。从验证一个简单的点击开始,逐步扩展到覆盖核心业务流程的复杂用例,过程中你会遇到各种奇怪的兼容性和稳定性问题。我的经验是,耐心查看Appium Server的控制台日志,那里面的错误信息通常非常具体,是解决问题的第一手资料。同时,保持你的工具链(Appium, 客户端库, 模拟器)版本相对稳定,并关注社区的常见问题,能帮你避开很多已知的坑。最后,别忘了将你的脚本和配置纳入版本控制(如Git),这是团队协作和持续集成的基石。