乐于分享
好东西不私藏

用大模型实现APP自动化测试:Android,iOS,HarmonyOS一次搞定

用大模型实现APP自动化测试:Android,iOS,HarmonyOS一次搞定

导读

一个测试新人的入职第一天

小张今年刚毕业,入职第一天被拉到项目群里。Leader发来一条消息:"抖音APP三端自动化你负责一下,下周出第一版。"

小张打开电脑,翻了三遍文档,发现一个问题——登录流程在Android上是用UiAutomator2写的,iOS上用的是XCUITest,HarmonyOS那边还在用老的API。同一个"滑到第二屏"的动作,Android是TouchAction(scroll),iOS是mobile: scroll,HarmonyOS又要调另一个库。三套代码,三个框架,三种写法。

这是行业常态。但今年不一样了。

大模型时代的到来,让我们终于有了一个选择——不再人肉翻译三端逻辑,而是让模型理解你的操作意图,自动生成对应平台的代码。你说"滑到第二屏",模型知道你要找什么元素、用什么方式滑、滑多远。一套自然语言,三端各自实现

💡这篇文章会给你一套完整可复用的方案:从三端模拟器安装,到Appium环境搭建,再到基于通义千问Qwen的Skill三件套封装,最后用抖音APP的完整流程做演示。看完就能动手。

文中附完整skill模版


第一章

痛点——为什么三端自动化是行业难题

同一登录功能——三端完全不同代码
同一登录功能,Android/iOS/HarmonyOS三套完全不同代码

先说结论:不是技术难,是一个人维护三个平台、三套代码体系,这是反人性的。

我们先看一份直观的对比:

维度
🤖 Android
🍎 iOS
📱 HarmonyOS
框架
UiAutomator2
XCUITest
DevEco Testing Toolkit
语言
Java / Kotlin / Python
Swift / Objective-C / Python
ArkTS / Java / Python
定位方式resource-id
 / xpath
accessibility-id
 / predicate
ohos:id
 / text
滑动APIUiScrollable / TouchActionmobile: scroll / swipedriver.swipe()
获取元素driver.find_element(By.ID)driver.find_element(By.ACCESSIBILITY_ID)driver.findComponent(By.id)

这种差异带来了三个核心矛盾:

矛盾一:命名混乱

同一个"登录按钮",Android上是resource-id="com.ss.../btn_login",iOS上是accessibility-label="LoginButton",HarmonyOS上可能又变成ohos:id="btn_login"。你在三套脚本里找同一个元素,要写三套定位器。

矛盾二:定位逻辑差异

Android上最稳的是resource-id;iOS上最推荐的是accessibility-id,xpath慢得让人崩溃;HarmonyOS API不太稳定,有时元素根本定位不到。每次换平台写脚本,大脑要切一次模式。

矛盾三:手势操作不同

"滑动到底部"在Android上可以用UiScrollable优雅滚动,iOS需要mobile: scroll配合方向参数写一大串,HarmonyOS还得推算坐标算百分比。同一个测试用例,三套脚本的滑动逻辑各走各路。

⚠️

根因:不是我们不会写自动化,而是三个平台各有一套独立的UI体系,它们的自动化框架来自不同的厂商、不同的年代、不同的设计哲学。测试工程师在做的,本质上是用三个不同的键盘打字。


第二章

大模型跨平台原理

传统方式 与 LLM方式对比
传统人翻译 vs LLM意图理解 — 工作方式本质差异

传统做法是:测试工程师写下一条自然语言用例,然后人肉翻译成三端代码。人工翻译意味着三倍工作量、三倍出错概率、三倍维护成本

LLM的做法完全不同:你写一条自然语言指令,大模型理解你的操作意图,然后分别生成三端代码。工程师只需要定义"做什么",模型来负责"怎么做"。

环节
❌ 传统方式(人翻译)
✅ LLM方式(意图理解)
需求输入
自然语言 → 人脑分析 → 写代码
自然语言直接输入
跨平台
三套独立脚本,各写各的
一套指令,自动生成三端代码
维护成本
修改用例要改三份
改自然语言描述,重新生成
知识门槛
需要精通三端框架
会写自然语言就行
一致性
各端细节靠人脑记忆对齐
从同一意图推导,天然对齐

为什么选通义千问Qwen?

市面上大模型很多,选Qwen的原因有四个:

  1. 中文理解强
     —— 自动化测试的注释、变量名、页面文案全是中文,Qwen对中文的语义理解在国内模型里排第一梯队;
  2. 免费额度充裕
     —— 个人开发者起步完全够用,不需要一开始就绑信用卡;
  3. Function Calling成熟
     —— Qwen的function call能力稳定,这是实现"自然语言→结构化指令→驱动Appium"的核心能力;
  4. 国内访问稳定
     —— 不需要挂代理,不会有能力降级或限流问题,生产环境落地更有保障。
Qwen + Appium + Python 三件套架构
Qwen + Appium + Python 三件套架构图
🏗️整体架构三板斧:Qwen负责理解意图和生成代码,Appium负责执行自动化操作,Python负责粘合逻辑。下面三步,搭建完整环境。

第三章

三端开发环境与模拟器安装

3.1 Android 模拟器——Windows

Android官方模拟器集成在Android Studio中,免费、稳定、和真机行为一致,是首选。

1.访问 developer.android.com/studio 下载Android Studio(建议选最新稳定版);
2.运行安装程序,安装时勾选 Android SDK 和 Android Virtual Device (AVD) 组件;
3.安装完成后打开Android Studio,点击右侧工具栏的 Device Manager
4.点击 Create device,选择 Pixel 7(性能适中、显示友好);
5.选择系统镜像:推荐 Android 14 (API 34),选不带"Google Play"的版本;
6.点击Finish,模拟器自动创建。在Device Manager中点绿色三角形启动。
💡备选方案:如果新手嫌Android Studio太重,可以用夜神模拟器yeshen.com)。图形化界面,一键安装,性能也不错。缺点是对Android新版本支持稍慢,API兼容性不如官方模拟器。但如果下一步只跑Appium自动化,夜神完全够用。

3.2 iOS 模拟器——macOS

⚠️重要提醒:iOS模拟器必须运行在macOS上,Windows不支持Xcode。如果你的主力机是Windows,有两个选择——一是跳过本章节只看Android和HarmonyOS部分,二是使用Mac云主机(如MacStadium或腾讯云Mac实例)。做iOS自动化开发,有一台Mac是硬门槛。

  1. 1. 打开 App Store,搜索 "Xcode" 并下载(安装包较大,约10GB,预留空间);

  2. 2. 安装完成后打开Xcode,进入 Settings → Platforms(旧版本在 Preferences → Components);

  3. 3. 在 Simulators 列表中,点击 "+" 号添加模拟器;

  4. 4. 选择 iPhone 15,iOS版本选最新的稳定版(目前iOS 17/18);

  5. 5. 从Xcode菜单栏选择 Xcode → Open Developer Tool → Simulator 启动模拟器。

启动后可以用 xcrun simctl list devices 确认模拟器列表,后续所有iOS自动化操作都会通过这个模拟器完成。

3.3 HarmonyOS Next 模拟器——Windows/macOS

这里需要先说明一个关键背景:HarmonyOS目前分两套系统。

一套是 HarmonyOS 4.x(兼容APK,可以跑Android应用)

另一套是 HarmonyOS Next(纯鸿蒙,不兼容APK)

本文针对 HarmonyOS Next

🔧工具:DevEco Studio(华为官方IDE,免费)

  1. 1. 访问 华为开发者官网 下载DevEco Studio(建议最新版);

  2. 2. 运行安装程序,安装过程中会自动提示安装 HarmonyOS SDK,全部勾选;

  3. 3. 安装完成后打开DevEco Studio,进入 Tools → SDK Manager

  4. 4. 在 SDK Platforms 页签中下载 HarmonyOS 4.0 系统镜像(这是目前Next最稳定的版本);

  5. 5. 进入 Tools → Device Manager,点击 Create emulator

  6. 6. 设备类型选择 Phone,选一个分辨率适中的型号,系统版本选刚下载的HarmonyOS 4.0镜像;

  7. 7. 点击Finish,启动模拟器。

💡注意:HarmonyOS模拟器启动后,通过 adb devices 同样能识别到设备(鸿蒙保留了ADB桥接协议),这样可以和Android共用一个 Appium 实例,减少环境维护量。


第四章

Appium环境配置

Appium 服务器连接三端模拟器
Appium服务作为统一入口,同时连接三端模拟器

Appium是连接三端的统一桥梁。配置好之后,一次启动,三端通联

Windows平台(适配 Android + HarmonyOS)

# ========== 1. 安装Node.js ==========# 访问 https://nodejs.org/ 下载LTS版本# ========== 2. 安装Appium主程序 ==========npm install -g appium# ========== 3. 安装Appium Python客户端 ==========pip install Appium-Python-Client# ========== 4. 安装Android UiAutomator2驱动 ==========appium driver install uiautomator2# ========== 5. 安装HarmonyOS驱动 ==========appium driver install gestural# ========== 6. 启动Appium服务(默认监听4723端口) ==========appium --allow-cors

macOS额外步骤(增加iOS支持)

# 安装XCUITest驱动(覆盖iOS设备)appium driver install xcuitest

验证连接

# 查看Android / HarmonyOS设备adb devices# 查看iOS模拟器列表xcrun simctl list devices
验证通过后,Appium日志里应该能看到对应设备的状态。至此,环境搭建完成。下一章我们会开始写真正能用起来的代码——基于Qwen的Skill三件套,让抖音APP三端自动化跑起来。

第五章

Skill三件套——约束LLM生成质量的核心

大模型直接生成代码,表面上"又快又全",但真正拿去跑,往往问题一堆:变量命名随心所欲、断言一个没有、定位器全是XPath逐层嵌套——这些不是模型不够聪明,而是没有给它划定边界。Skill三件套(SKILL.md + spec.yaml + template)就是这套约束机制的核心。

5.1 为什么需要 Skill

我们在实际项目中观察到大模型生成Appium代码时的高频问题:

  • 命名随意。
     模型随手生成 click_login_buttonhandlePopupswipe_up_2 这样的变量名,第一次看能懂,三个月后维护时全是坑。更要命的是三端命名风格完全不一致。
  • 断言缺失。
     几乎所有模型默认不写断言——操作完就结束了,根本不验证"操作是否成功"。这是一个致命习惯,尤其在CI流水线里,没有断言的测试等于没有测试
  • 定位器不稳定。
     模型偏爱XPath,几乎不用resource-id或accessibility_id。一旦UI改版,XPath断链,脚本直接崩溃。
  • 隐式等待缺失。
     模型习惯用固定sleep,不了解Appium的显式等待机制,导致间歇性失败。

🧠解决思路:SKILL.md负责告诉模型"你是谁",spec.yaml负责告诉模型"你只能这样输出",template负责告诉模型"代码骨架已经定好,你只填参数"。三层约束叠加,生成质量才可控。

5.2 SKILL.md 完整内容

# SKILL.md — 跨平台移动端自动化代码生成专家name: app-automation-expertversion: "1.0"description: 跨平台移动端自动化测试代码生成专家,专注于 Android(Appium Python)、iOS(Appium XCUITest Python)、HarmonyOS(DevEco Testing ArkTS)三端自动化脚本生成与优化。## 角色定义你是一名资深移动端自动化测试工程师,精通 Android、iOS、HarmonyOS 三大平台的 UI 自动化框架,具备以下能力:- 熟练使用 Appium(Android/iOS)进行跨平台自动化测试- 熟练使用 DevEco Testing(HarmonyOS)进行 ArkTS 自动化测试- 深度理解 PageObject 设计模式与关键字驱动测试框架- 能够根据 UI 描述或截图生成稳定可靠的元素定位器- 能够处理复杂场景:多平台差异、弹窗、等待、异常恢复## 核心职责生成符合 spec.yaml 规范的 Android/iOS、HarmonyOS 自动化代码,确保:1. 代码可直接运行,无需额外修改2. 元素定位器优先使用稳定策略(id → accessibility_id → text → class)3. 每个操作步骤后必须有断言验证4. 三端代码风格统一,命名规范一致5. 包含完整的错误处理和日志输出## 三端统一规范要点### 命名格式| 类型 | Android/iOS | HarmonyOS ||------|-------------|-----------|| 测试用例 | test_业务动作_预期结果 | async test业务动作_预期结果() || 测试类 | Test{业务}Page | @Component struct {业务}Page || 元素定位器变量 | loc_{页面}_{元素} | loc_{页面}_{元素} |### 断言要求- 技术层断言:每个 UI 操作后必须验证元素存在或消失- 业务层断言:关键业务节点必须验证页面状态(如列表非空、搜索结果 > 0)- 格式统一:Android/iOS 使用 assertEqual / assertTrue,HarmonyOS 使用 expect()### 禁止事项- ❌ 禁止使用硬编码 sleep,必须使用显式等待(WebDriverWait / until)- ❌ 禁止使用绝对坐标点击,必须使用元素定位- ❌ 禁止在弹窗处理前不检查弹窗是否存在- ❌ 禁止生成没有断言的测试用例- ❌ 禁止使用 XPath 绝对路径,必须使用相对定位(id、text、class 组合)## 默认等待时间配置- 元素查找显式等待:10 秒- 页面加载等待:5 秒- 滑动操作后等待:1 秒- 弹窗检测最大重试:3 次(每次间隔 1 秒)---# 【关键提示】每次生成前必须完成以下检查清单1. [ ] 确认被测平台(Android / iOS / HarmonyOS)2. [ ] 确认操作序列完整(启动 → 操作 → 断言 → 退出)3. [ ] 检查每个操作步骤是否有对应断言4. [ ] 检查定位器是否遵循优先级规范5. [ ] 确认代码可以直接复制运行6. [ ] 确认截图保存路径规范且目录存在

5.3 spec.yaml 完整内容

# spec.yaml — 跨平台移动端自动化代码生成规范# 版本:1.0 | 适用平台:Android / iOS / HarmonyOSmetadata:  last_updated: "2025-01-15"  maintainer: QA Team  version: "1.0"# ==================== 命名规范 ====================naming_conventions:  android:    test_case: "test_{module}_{action}_{expected}"    test_class: "Test{Module}Page"    locator_variable: "loc_{page}_{element}"  ios:    test_case: "test_{module}_{action}_{expected}"    test_class: "Test{Module}Page"    locator_variable: "loc_{page}_{element}"  harmonyos:    test_function: "async test{Module}{Action}{Expected}()"    component_struct: "{Module}Page"    locator_variable: "loc{Page}{Element}"# ==================== 元素定位器优先级 ====================locator_priority:  levels:    - name: "优先级1:ID / Resource-ID(最稳定)"      android: "By.id, driver.find_element_by_id"      ios: "By.ACCESSIBILITY_ID(推荐)"      harmonyos: "driver.findComponent(By.id)"    - name: "优先级2:Accessibility ID"      android: "By.accessibility_id"      harmonyos: "By.accessibility"    - name: "优先级3:Text / Label 文本匹配"      android: "By.xpath with contains(@text, '...')"      ios: "By.name"      harmonyos: "By.text"    - name: "优先级4:Class + Index 组合"      android: "By.xpath with class + index"      ios: "By.class_name + index"      harmonyos: "By.type + index"    - name: "优先级5:坐标(最后手段)"      android: "TouchAction + tap(x, y)"      ios: "XCUIElement.coordinate(x: y:)"      harmonyos: "driver.tap([x, y])"# ==================== 断言规范 ====================assertion_rules:  technical:    - "元素出现:assertTrue(element.is_displayed())"    - "元素消失:assertFalse(exists)"    - "元素可点击:assertTrue(element.is_enabled())"  business:    - "搜索结果数 > 0:assertTrue(len(results) > 0)"    - "页面标题正确:assertEqual(driver.title, '期望标题')"    - "列表非空:assertTrue(len(list_items) > 0)"# ==================== 禁止事项 ====================forbidden_patterns:  - rule: "禁止硬编码 sleep"    correct: "WebDriverWait(driver, 10).until(EC.presence_of_element_located(...))"    wrong: "time.sleep(3)  # ❌ 禁止"  - rule: "禁止 XPath 绝对路径"    correct: "By.id('com.example:id/btn_confirm')"    wrong: "//android.widget.Button[3]/android.view.View[1]  # ❌ 禁止"# ==================== 等待配置 ====================wait_config:  explicit_wait: 10       # 元素查找显式等待(秒)  page_load_wait: 5       # 页面加载等待(秒)  swipe_wait: 1          # 滑动后稳定等待(秒)  popup_retry: 3          # 弹窗检测重试次数

5.4 template 代码模板概述

template是SKILL.md + spec.yaml约束体系的最后一道防线:告诉LLM"代码骨架已经写好,你只负责填参数"。

  • template_android.py.j2
     — Appium Android Python模板。基于PageObject模式构建,包含测试类骨架、Desired Capabilities配置区、基础工具方法(显式等待、截图、弹窗检测),LLM只需在占位区域填充业务逻辑。
  • template_ios.py.j2
     — Appium XCUITest Python模板。与Android模板结构一致,但定位器策略和部分API(如swipe_up、alert处理)替换为iOS原生实现。
  • template_harmony.py.j2
     — DevEco Testing ArkTS模板。以@Component struct为页面组件单位,内置driver操作封装和expect断言风格,整体思路与Appium模板一致。

三端模板统一的结构:测试入口层(Setup/Teardown)→ 业务页面对象层(PageObject)→ 元素定位器层(locator字典)→ 操作断言层(step + assert)。LLM在这个骨架内填充具体参数,始终不会跳出框架。

5.5 三件套配合工作流程

Skill 三件套配合流程图
Skill三件套配合流程 — SKILL.md定身份、spec.yaml划边界、template给骨架

第一步:SKILL.md → 角色定义。 加载SKILL.md,LLM明确自己的身份是"跨平台移动端自动化专家",知道需要生成完整可运行的三端代码,知道PageObject模式和断言要求。

第二步:spec.yaml → 输出边界约束。 spec.yaml作为上下文注入,LLM的输出边界被严格约束:命名格式、定位器优先级、禁止事项全部内置,生成时自动遵守。

第三步:template模板 → 代码骨架。 template提供具体语言的代码框架,LLM只需要在占位符里填入具体参数(元素定位符、文本内容、超时时间等),而不是从头构建整个文件。


第六章

Douyin全流程实战演示

Douyin 自动化测试完整流程
Douyin自动化测试完整流程 — 从启动到退出的8个步骤
测试场景:
APP拉起 → 关闭隐私弹窗 → 向上滑动切换视频 → 点击搜索栏 → 输入"APP自动化测试" → 执行搜索 → 截图保存结果 → 退出APP

6.1 Android端完整代码

"""douyin_android.py — 抖音APP自动化测试(Android)依赖:pip install Appium-Python-Client运行:python douyin_android.py"""import os, timefrom appium import webdriverfrom appium.options.android import UiAutomator2Optionsfrom appium.webdriver.common.appiumby import AppiumByfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECDOUYIN_PACKAGE = "com.ss.android.ugc.aweme"DOUYIN_ACTIVITY = ".main.MainActivity"SCREENSHOT_DIR = "./screenshots/android"os.makedirs(SCREENSHOT_DIR, exist_ok=True)def get_driver():    caps = UiAutomator2Options()    caps.platform_name = "Android"    caps.device_name = "Android_Emulator"    caps.app_package = DOUYIN_PACKAGE    caps.app_activity = DOUYIN_ACTIVITY    caps.no_reset = False    caps.auto_grant_permissions = True    return webdriver.Remote("http://localhost:4723", options=caps)def dismiss_privacy_popup(driver):    """关闭隐私协议弹窗(首次启动必须处理)"""    try:        for text in ["同意""确认""好的""允许"]:            btn = WebDriverWait(driver, 3).until(                EC.element_to_be_clickable(                    (AppiumBy.ANDROID_UIAUTOMATOR, f'textContains("{text}")')                )            )            btn.click()            time.sleep(1)            break    except Exception:        pass  # 无弹窗或已处理def swipe_up(driver, duration=800):    """向上滑动一页视频"""    size = driver.get_window_size()    x = size["width"] // 2    start_y = int(size["height"] * 0.75)    end_y = int(size["height"] * 0.25)    driver.swipe(start_x=x, start_y=start_y, end_x=x, end_y=end_y, duration=duration)    time.sleep(1.5)def click_search_icon(driver):    """点击搜索入口"""    try:        search_icon = WebDriverWait(driver, 10).until(            EC.element_to_be_clickable(                (AppiumBy.ID, f"{DOUYIN_PACKAGE}:id/search_icon")            )        )    except Exception:        search_icon = WebDriverWait(driver, 5).until(            EC.element_to_be_clickable(                (AppiumBy.ANDROID_UIAUTOMATOR, 'textContains("搜索")')            )        )    search_icon.click()    time.sleep(1)def search_and_capture(driver, keyword):    """输入关键词搜索并截图"""    search_input = WebDriverWait(driver, 10).until(        EC.presence_of_element_located(            (AppiumBy.ID, f"{DOUYIN_PACKAGE}:id/search_edit")        )    )    search_input.clear()    search_input.send_keys(keyword)    search_btn = WebDriverWait(driver, 5).until(        EC.element_to_be_clickable(            (AppiumBy.ANDROID_UIAUTOMATOR, 'text("搜索")')        )    )    search_btn.click()    time.sleep(3)    timestamp = time.strftime("%Y%m%d_%H%M%S")    screenshot_path = os.path.join(SCREENSHOT_DIR, f"search_result_{timestamp}.png")    driver.get_screenshot_as_file(screenshot_path)    print(f"截图已保存: {screenshot_path}")    return screenshot_pathdef test_douyin_full_flow():    driver = get_driver()    try:        dismiss_privacy_popup(driver)   # Step1: 关闭隐私弹窗        time.sleep(2)        swipe_up(driver)                 # Step2: 向上滑动        click_search_icon(driver)       # Step3: 点击搜索入口        time.sleep(2)        search_and_capture(driver, "APP自动化测试")  # Step4: 搜索        print("✅ Android端Douyin自动化流程执行完成")    finally:        driver.terminate_app(DOUYIN_PACKAGE)  # Step5: 退出APP        driver.quit()if __name__ == "__main__":    test_douyin_full_flow()

运行说明

依赖包:pip install Appium-Python-Client selenium
运行命令:python douyin_android.py
前提:确保Appium服务已启动(appium),模拟器/设备已通过adb devices连接。

6.2 iOS端完整代码

"""douyin_ios.py — 抖音APP自动化测试(iOS)依赖:pip install Appium-Python-Client运行:python douyin_ios.py"""import os, timefrom appium import webdriverfrom appium.options.ios import XCUITestOptionsfrom appium.webdriver.common.appiumby import AppiumByfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECDOUYIN_BUNDLE_ID = "com.ss.android.ugc.aweme"SCREENSHOT_DIR = "./screenshots/ios"os.makedirs(SCREENSHOT_DIR, exist_ok=True)def get_driver():    caps = XCUITestOptions()    caps.platform_name = "iOS"    caps.device_name = "iPhone 15"    caps.bundle_id = DOUYIN_BUNDLE_ID    caps.no_reset = False    caps.automation_name = "XCUITest"    return webdriver.Remote("http://localhost:4723", options=caps)def dismiss_att_popup(driver):    """关闭iOS ATT隐私弹窗"""    try:        alert = WebDriverWait(driver, 3).until(EC.alert_is_present())        alert.accept()        time.sleep(1)    except Exception:        try:            allow_btn = driver.find_element(                AppiumBy.IOS_PREDICATE, 'name CONTAINS "允许"'            )            allow_btn.click()        except Exception:            passdef swipe_up_ios(driver):    """向上滑动(iOS专用)"""    size = driver.get_window_size()    x = size["width"] // 2    start_y = int(size["height"] * 0.75)    end_y = int(size["height"] * 0.25)    driver.execute_script("mobile: swipe", {        "startX": x, "startY": start_y,        "endX": x, "endY": end_y, "duration"0.8    })    time.sleep(1.5)def click_search_ios(driver):    """点击搜索入口(iOS版)"""    try:        search_icon = WebDriverWait(driver, 10).until(            EC.element_to_be_clickable(                (AppiumBy.IOS_PREDICATE,                 'label CONTAINS "搜索" OR name CONTAINS "search"')            )        )    except Exception:        search_icon = WebDriverWait(driver, 5).until(            EC.element_to_be_clickable(                (AppiumBy.CLASS_NAME, "XCUIElementTypeTextField")            )        )    search_icon.click()    time.sleep(1)def search_and_capture_ios(driver, keyword):    """输入关键词搜索并截图"""    search_input = WebDriverWait(driver, 10).until(        EC.presence_of_element_located(            (AppiumBy.CLASS_NAME, "XCUIElementTypeTextField")        )    )    search_input.clear()    search_input.send_keys(keyword)    search_btn = driver.find_element(        AppiumBy.IOS_PREDICATE, 'name == "Search"'    )    search_btn.click()    time.sleep(3)    timestamp = time.strftime("%Y%m%d_%H%M%S")    screenshot_path = os.path.join(SCREENSHOT_DIR, f"search_result_{timestamp}.png")    driver.get_screenshot_as_file(screenshot_path)    print(f"截图已保存: {screenshot_path}")    return screenshot_pathdef test_douyin_ios_full_flow():    driver = get_driver()    try:        dismiss_att_popup(driver)       # Step1: 关闭ATT隐私弹窗        time.sleep(2)        swipe_up_ios(driver)           # Step2: 向上滑动        click_search_ios(driver)        # Step3: 点击搜索入口        time.sleep(2)        search_and_capture_ios(driver, "APP自动化测试")  # Step4        print("✅ iOS端Douyin自动化流程执行完成")    finally:        driver.terminate_app(DOUYIN_BUNDLE_ID)        driver.quit()if __name__ == "__main__":    test_douyin_ios_full_flow()

6.3 HarmonyOS端完整代码

// douyin_harmony.ts — 抖音APP自动化测试(HarmonyOS Next)// 运行环境:DevEco Studio + DevEco Testing Toolkit// 语言:ArkTSimport driver from '@ohos.driver'import app from '@ohos.app.ability.Want'const BUNDLE_NAMEstring = "com.ss.android.ugc.aweme"const SCREENSHOT_DIRstring = "./screenshots/harmony"export class DouyinHarmonyTest {  private searchKeywordstring = "APP自动化测试"  async dismissPrivacyPopup(): Promise {    try {      let agreeBtn = await driver.findElement({        "strategy": { "type""text""value""同意" }      })      if (agreeBtn) { await agreeBtn.click() }    } catch (e) {      console.info("隐私弹窗不存在或已处理")    }    await driver.delay(1000)  }  async swipeUp(): Promise {    let rect = await driver.getDisplayBounds()    let x = rect.width / 2    let startY = rect.height * 0.75    let endY = rect.height * 0.25    await driver.swipe(      Math.round(x), Math.round(startY),      Math.round(x), Math.round(endY), 800    )    await driver.delay(1500)  }  async clickSearchIcon(): Promise {    let searchIconany = null    try {      searchIcon = await driver.findElement({        "strategy": { "type""id""value""search_icon" }      })    } catch (e) {      searchIcon = await driver.findElement({        "strategy": { "type""text""value""搜索" }      })    }    if (searchIcon) { await searchIcon.click() }    await driver.delay(1000)  }  async searchAndCapture(): Promise {    let searchInputany = await driver.findElement({      "strategy": { "type""className""value""TextInput" }    })    if (searchInput) { await searchInput.inputValue(this.searchKeyword) }    await driver.delay(500)    let searchBtn = await driver.findElement({      "strategy": { "type""text""value""搜索" }    })    if (searchBtn) { await searchBtn.click() }    await driver.delay(3000)    let timestamp = Date.now().toString()    let path = `${SCREENSHOT_DIR}/search_result_${timestamp}.png`    await driver.captureScreen(path)    console.info("截图已保存: " + path)  }  async quitApp(): Promise {    await driver.terminateAbility(BUNDLE_NAME)  }  async testFullFlow(): Promise {    console.info("=== HarmonyOS Douyin自动化开始 ===")    await this.dismissPrivacyPopup()   // Step1    await this.swipeUp()                // Step2    await this.clickSearchIcon()        // Step3    await driver.delay(2000)    await this.searchAndCapture()       // Step4    await this.quitApp()                // Step5    console.info("=== HarmonyOS Douyin自动化完成 ===")  }}export default new DouyinHarmonyTest()

6.4 三端关键差异总结

三端代码关键差异对照表
三端关键API差异对照表 — 一目了然各端实现差异
操作
🤖 Android
🍎 iOS
📱 HarmonyOS
拉起APPstart_activity(pkg,act)activate_app(bundleId)startAbility(bundle)
隐私弹窗
坐标点击"同意"按钮
alert.accept()promptAction
 API
向上滑动driver.swipe()mobile: swipedriver.swipe(x,y,x,y)
输入文本send_keys(text)send_keys(text)inputValue(text)
截图保存get_screenshot_as_file()get_screenshot_as_file()captureScreen(path)
退出APPterminate_app(pkg)terminate_app(bundleId)terminateAbility()

第七章

三端特殊场景处理

7.1 隐私弹窗与权限授权

平台
弹窗类型
推荐处理方式
Android
隐私协议弹窗
首次启动检测,坐标点击"同意"按钮
iOS
ATT弹窗(App Tracking Transparency)
driver.switch_to.alert.accept()
HarmonyOS
权限请求弹窗
promptAction.showAlertDialog()
 或坐标点击

⚠️务必优先处理隐私弹窗这类弹窗往往出现在应用首次启动、版本升级或清空数据之后。如果不在脚本最前面统一拦截并关闭,后续所有点击、输入、断言操作都会被遮挡或中断。建议把"启动后先消弹窗"封装成公共前置步骤,所有用例复用。

7.2 元素定位器三端差异

  • Android:resource-id
    优先,其次使用uiautomator2的文本定位(UiSelector().text("xxx"))。
  • iOS:accessibility-id
    优先(最稳定、最不易随UI变动),其次使用XCUIElementQuery链式调用。
  • HarmonyOS:ohos:id
    优先,其次使用ArkUI Selector进行组件匹配。

💡通用技巧:让LLM生成定位代码后,务必分别用Appium Inspector(Android/iOS)与DevEco Inspector(HarmonyOS)实际验证定位符的正确性,不要照搬生成结果。

三端元素定位工具
Appium Inspector / Xcode / DevEco Inspector三端元素定位工具

7.3 等待策略与稳定性优化

核心原则:显式等待优先,禁止time.sleep()硬编码等待。固定sleep既拖慢执行又掩盖时序问题,是自动化不稳定的根源。

  • Android / iOS:
    使用WebDriverWait配合expected_conditions,等待元素可见、可点击等条件成立。
  • HarmonyOS:
    使用waitForElement并配置合理超时,等待组件树渲染完成。

💡iOS特殊说明:XCUITest对动态内容(如列表滚动加载、动画过渡)支持相对较弱,定位与断言失败率偏高。实践中应适当增加等待时间,建议显式等待超时设为10–15秒,避免"刚好差一点点"的偶发失败。稳定优先于速度。


第八章

团队落地路径与工具链

技术选型只是第一步,真正落地要靠清晰的推进节奏。建议按"四步走"路径稳步推进,先验证再放大。

第一步:试点验证(第1–2周)

选定登录流程作为第一个试点场景,记录LLM生成代码的直接可用率。目标:直接可用率 ≥ 80%。试点价值在于摸清大模型在本团队真实项目上的产出质量,而不是追求覆盖面。

第二步:规范迭代(第3–4周)

统计实际可用率,把低于80%的场景逐一剖析,将反复出错的模式补充到spec.yaml禁止事项中,持续迭代规范。规范越具体,后续生成质量越稳定。

第三步:Prompt资产积累(持续)

将不同场景的Prompt模板沉淀为团队资产,形成可复用的目录结构:

prompts/├── login.yaml          # 登录场景├── search.yaml         # 搜索场景├── swipe.yaml          # 滑动操作└── payment.yaml        # 支付流程

第四步:CI/CD集成(长期)

将自动化测试接入持续集成,构建—测试—报告闭环:

GitHub Actions / Jenkins  → 触发 Appium 测试  → Allure 报告(Android/iOS)  → DevEco Test Report(HarmonyOS)
🎯至此,测试从"个人脚本"升级为"团队基础设施",新人也能快速复用既有资产,维护成本大幅降低。