Appium Java 测试编写器
生成看起来像 SDET 亲自编写的移动端测试:一套代码同时支持 Android 和 iOS,设备间并行安全,没有 sleep,手势兼容 java-client 9+,Allure 报告可追溯到手动用例 ID。同时处理 Python → Java 迁移,这是一个常见的实际任务,值得忠实地完成,而不仅仅是逐行翻译。
确定模式
- 手动用例 → Java:遵循工作流 A。
- Python/Appium 代码 → Java:遵循工作流 B。首先阅读
references/python-to-java-migration.md。 - 搭建框架:生成
references/framework-scaffold.md中的文件,并根据用户的应用程序和平台进行调整。
如果用户分享了任何现有的 Java 框架文件(BaseTest、屏幕类、testng.xml、能力配置),请优先遵循他们的约定,而不是参考文件中的约定。
工作流 A:手动用例 → 测试
- 规范化每个用例:ID、标题、优先级、平台、前置条件、测试数据、步骤、预期结果。注意特定于平台的步骤(Android 返回按钮、iOS 权限提示)。
- 屏幕和组件:每个屏幕一个类;可复用的小部件(底部导航栏、工具栏、底部弹窗)作为组件类,组合到屏幕类中。屏幕方法表达用户意图,并带有 Allure
@Step("...")。导航动作返回下一个屏幕对象。 - 定位器 — 通过同一个字段上的注解实现跨平台:
优先级:accessibility id → Android resource-id / iOS name → UiAutomator / iOS predicate 或 class chain → XPath 最后(在 iOS 上很慢,到处都脆弱;若使用请注释原因)。如果未提供页面源代码、Appium Inspector 输出或截图,则将猜测标记为@AndroidFindBy(accessibility = "login-button") @iOSXCUITFindBy(accessibility = "login-button") private WebElement loginButton;// TODO-VERIFY locator。如果只知道一个平台的定位器,请编写该定位器,并将另一个注解留为TODO-VERIFY,而不是盲目复制。 - 测试:每个用例一个
@Test,方法名体现行为,使用@TmsLink关联用例 ID,@Epic/@Feature/@Story/@Severity(P0 → BLOCKER/CRITICAL,P1 → NORMAL,P2+ → MINOR)。数据变体 → 使用@DataProvider,第一列为用例 ID,并在方法体内使用Allure.link(caseId, "tms", caseId)。每个预期结果都成为一个断言。 - 设备/应用状态:指出需要非默认能力的用例(权限对话框需要
autoGrantPermissions=false,引导流程需要干净安装 /noReset=false,仅限模拟器的功能如指纹识别),并将它们放在单独的组中或注明覆盖。 - 前置条件:优先使用深度链接(Android 上
driver.get("myapp://cart"),mobile: deepLink)或 API 设置,而不是冗长的点击序列。尽可能通过 API/会话实现登录前置条件。 - 不可自动化(真实短信 OTP、真实支付、无模拟器支持的生物识别、主观视觉检查):在“未自动化”下列出,并附上替代方案(测试 OTP、沙箱支付、模拟器/仿真器上的
mobile: enrollBiometric/sendBiometricMatch、视觉比较工具)。
工作流 B:Python → Java 迁移
- 阅读
references/python-to-java-migration.md。 - 首先映射结构(conftest fixtures → BaseTest/工厂,页面模块 → 屏幕类,
parametrize→@DataProvider),然后翻译代码。 - 修复反模式,而不是照搬:
time.sleep→ 显式等待,TouchAction/MultiAction→ W3C Actions 或mobile:手势,desired-capabilities 字典 →UiAutomator2Options/XCUITestOptions,全局驱动 → ThreadLocal。 - 保持测试意图和断言完全相同。如果 Python 测试断言较弱(例如
assert element)或隐藏了失败(try/except: pass,条件中的断言),请修复它并在迁移表中指出,因为原始测试可能从未能够失败。 - 输出一个迁移表:
Python 文件/函数 | Java 类/方法 | 除翻译外的更改。
等待:显式且基于条件
- 隐式等待设置为 0。混合隐式等待和显式等待会导致不可预测的超时。
- 永远不要
Thread.sleep。使用WebDriverWait和ExpectedConditions(visibilityOf、elementToBeClickable、invisibilityOf、textToBePresentInElement、attributeToBe),通过BaseScreen中的辅助方法。 - 等待加载器/旋转指示器消失,而不是等待时间过去。
- 超时时间来自配置,而非内联数字。
- 在导致列表或轮播重新渲染的动作之后,等待一个之后始终为真的信号(弹窗关闭、加载器消失、列表出现、计数等于预期)。不要等待“第一项文本已更改”:预期值可能已经存在,导致等待超时。BaseScreen 的等待忽略
StaleElementReferenceException,因此在条件内部重新查询。
手势
java-client 9 移除了 TouchAction/MultiAction。请使用:
- 滚动到元素:Android
AppiumBy.androidUIAutomator("new UiScrollable(...).scrollIntoView(...)");iOSmobile: scroll配合predicateString。 - 滑动/拖拽/长按:平台
mobile:手势(Androidmobile: swipeGesture、longClickGesture;iOSmobile: swipe、touchAndHold)或BaseScreen中用于跨平台的 W3CPointerInput序列。 参见references/framework-scaffold.md→ 手势。
并行安全
驱动存在于 ThreadLocal<AppiumDriver> 中,每个测试(或如果用户要求速度,每个类)创建一次,在 quit() 后始终 remove()。每个并行设备需要自己的 udid 和唯一的 systemPort(Android)/ wdaLocalPort(iOS),来自 testng.xml 参数。没有静态驱动或静态元素状态。
输出格式
- 文件:完整的可编译文件,包含导入,每个文件在其路径标题下。如果超过 ~3 个文件或用户希望下载,则将它们写入磁盘作为项目文件夹。
- 追溯表(工作流 A)或迁移表(工作流 B)。
- 未自动化 / 差距。
- 需要验证:
TODO-VERIFY定位器的计数和列表、假设的能力(应用路径、包/活动、bundle ID)、仅平台步骤。
保持描述简洁;代码是交付物。
机密性
对于作品集/公共工作,请使用包 com.example(或用户的包)、一个公共演示应用,并将任何公司名称、内部应用 ID、内部 URL、真实电话号码/电子邮件或专有事件名称替换为中性名称。在一行中说明替换。
参考文件
references/framework-scaffold.md:pom.xml、配置、包含 ThreadLocal + Options 类的 DriverFactory、包含失败时 Allure 截图/页面源代码的 BaseTest、包含等待辅助方法和手势的 BaseScreen、用于并行设备的 testng.xml。references/python-to-java-migration.md:Python Appium/pytest 习惯用法到 Java/TestNG 的并排映射,以及一个实际迁移示例。references/example-swaglabs.md:手动用例 → 屏幕 → 测试,针对公共 Sauce Labs 示例应用。