讨论稳定定位之前,先问一个具体问题:测试脚本依赖某个 x/y 坐标,页面内容增减或不同屏幕密度使点击落在错误按钮上?答案不能只从界面效果判断。需要自动回归市政服务大厅排队取号页面有真实操作顺序,必须从触发、运算、反馈三个环节理解。

技术主题封面

真正需要回答的问题

坐标点击是自动化的诱惑,因为录制脚本时最直观:找到按钮位置,点击那个像素。然而 UI 只要换行、增加公告或者适配窄屏,原来的坐标就可能点到另一个控件。自动化用例需要表达业务意图而不是截图几何。

“自动化用例应优先表达“找到某个可识别组件并执行行为”,而不是绑定一次截图的像素位置”是这篇最重要的设计判断。单看成功路径,需要自动回归市政服务大厅排队取号页面可以靠若干事件回调工作;但只要用户中途修改、取消或重复操作,若没有清晰约束,就无法确定最终输出究竟属于哪一次输入。工程拆分必须以这些不确定性为起点。

我们希望得到的不是一张孤立的效果图,而是一条可追溯的处理链。整个过程拆为启动页面、等待组件、语义定位、执行点击、检查反馈。每一段只承诺自己负责的结果,下一段不靠猜测前一步的内部状态。对稳定定位而言,这种清晰分工也是回归测试能够写得具体的前提。

给关键控件设置稳定标识,可以把“取号按钮”作为逻辑目标。标识不应包含姓名、队列号码等动态个人信息,也不应依赖当前语言的标题文本,否则国际化后测试会大面积失效。

先区分几个容易混淆的概念

实施选择是:为关键业务控件分配稳定且无敏感信息的标识,在测试中优先按可辨识特征定位,并对异步页面等待实际状态。这里不能倒过来,先把组件和请求写完,再向现有代码里硬塞判断。规则入口只接收必要数据,输出也尽量可序列化。这样在没有设备的环境下仍能对核心逻辑做确定性检查。

当路径执行到“启动页面”,先区分用户仍在编辑、数据已经校验和动作已经确认三种事实。随后执行“等待组件”时,使用同一份中间结果才能避免旧数据回流。日志或测试也应记录阶段,而不是只看最终页面有没有消失。

把“等待组件”到“语义定位”视作可中断的边界。任何阶段都可能遇到无效输入或设备状态变化,恢复时应回到上一个仍然可信的事实,而不是为了显示流畅强行跳到最终步骤。这样的路径更适合实际调试。

把“语义定位”和“执行点击”分成两个阶段,是为了让每个阶段都有可检查的输入与输出。需要自动回归市政服务大厅排队取号页面并不需要复杂框架才能做到这一点,真正需要的是一个稳定的数据契约:前一步产出的值能被下一步清楚识别,失败时也能停在正确的位置。

异步页面必须等待目标状态而不是 sleep 固定两秒。网络返回和页面渲染时间不恒定,固定等待既浪费时间又可能在慢设备失败;真正稳健的条件是“取号成功状态已出现”,然后再断言相关内容。

稳定定位的关键设计

核心做法与代码

下面给出可以独立检查的关键实现。代码只覆盖本篇要证明的核心行为;涉及工程权限、服务端协议或构建设置的条件已在上下文列明。它应被放入明确的模块中,而不是把所有业务逻辑堆进 ArkUI build()。

// UI 与自动化用例共同约定稳定且无个人信息的识别标记。
const ID_TAKE_TICKET: string = 'ticket_take_button'
const ID_QUEUE_STATUS: string = 'ticket_queue_status'
@Entry
@Component
struct QueuePage {
  @State issued: boolean = false
  build() {
    Column({ space: 20 }) {
      Button('领取排队号')
        .id(ID_TAKE_TICKET)
        .onClick(() => { this.issued = true })
      Text(this.issued ? '取号成功' : '尚未取号')
        .id(ID_QUEUE_STATUS)
    }.padding(24)
  }
}

定位策略要有层级:能使用稳定控件标识时优先使用;缺少标识时使用有业务含义的可访问属性;最后才考虑文本或结构路径。不要把列表中的第几个孩子作为长期契约。

测试侧应使用当前 UiTest 的 Driver 与 ON,而不是已弃用的坐标定位旧接口。下面的调用放入 ohosTest 的测试用例中,启动目标页面后执行;断言针对业务状态,而非固定睡眠时间。

import { Driver, ON } from '@kit.TestKit'
async function assertTicketAction(): Promise<void> {
  const driver: Driver = Driver.create()
  const button = await driver.findComponent(ON.id('ticket_take_button'))
  await button.click()
  const status = await driver.findComponent(ON.id('ticket_queue_status'))
  const text = await status.getText()
  if (text !== '取号成功') throw new Error('取号状态不匹配')
}

代码中的关键判断来自业务约束,而不是某个屏幕比例。以“语义定位”为界,输入应先满足所属阶段的前置条件;当判断失败时,返回错误或保留状态比直接抛出无法解释的页面状态更可控。对需要 UI 反馈的部分,随后由组件转换为明确的文案。

如果把上述核心逻辑接到一个 ArkUI 页面,推荐组件仅处理状态投影和交互事件:例如 TextInput.onChange 接收新的字符串,Button.onClick 触发一次显式动作,得到结果后再设置 @State 驱动展示。对于不是输入框的稳定定位功能,则采用相同的单向数据边界而不强行引入 TextInput。

稳定定位的验证示例

常见误判怎么避开

最应该提前考虑的风险是:仅靠文案匹配容易受多语言影响,使用随机组件索引同样会使测试脆弱。这句话不是发布时随手附加的注意事项,而是编码时必须坚持的限制。只要异常路径缺乏明确处理,下一次回归就可能把一个本来可恢复的状态变成难以判断的失败。

验证时改动按钮颜色、宽度和所处容器,再运行同一逻辑用例。只要控件的业务身份未变,测试就不应该失败;如果按钮真正改变行为,则相应更新契约与预期。

状态验收。 在需要自动回归市政服务大厅排队取号页面中,专门制造一个尚未完成的状态:测试脚本依赖某个 x/y 坐标,页面内容增减或不同屏幕密度使点击落在错误按钮上。观察模块是否明确阻止下一动作、是否保留用户已经输入的有效内容,以及重新操作后是否会得到可解释的新结果。这个检查的价值在于不把一次成功操作当作稳定性的全部证据。

逆序验收。 把正常操作反过来执行,例如先触发执行点击,再补齐语义定位所需要的条件。若界面没有把错误留在错误位置,或者隐式使用上一轮结果,就说明阶段约束并未完全落实。真正的测试应覆盖操作顺序变化,而非只按产品示例步骤点击。

重复验收。 连续两次执行执行点击,再执行检查反馈,检查第二次动作是新意图还是前次动作的继续。对于稳定定位尤其要明确重复处理规则;“按钮点了两次”是用户行为事实,并不自动意味着应当产生两份业务副作用。

错误恢复。 在启动页面成功、启动页面尚未完成的位置构造异常。应能说明数据暂存在哪里,用户看到何种反馈,是否允许重新触发,以及什么条件下必须停止。不要为了追求一张好看的页面而隐藏正在等待确认的真实状态。

可复核的结论

文中给出的是生产页面的最小可测试入口。实际的 UI 测试驱动 API 与工程目录,应按当前测试框架版本从官方文档配置,不把不确定的方法名写成已经运行过的现成脚本。

本篇可用以下结果作为验收目标:改变屏幕宽度和按钮排列后,相同用例仍能选择正确取号操作。检验时先从固定输入入手,再加入中途切换、重复操作和失败恢复。可解释的错误结果也属于正确实现的一部分;没有必要为了让每个步骤都显示绿色而悄悄跳过不满足的前置条件。

还有一个容易被忽视的协作问题:页面状态、领域规则和最终输出往往由不同代码文件甚至不同开发者维护。要让改动可控,必须为稳定定位明确最小接口,规定哪些数据可以跨模块传递、哪些只是展示层的临时变量;代码评审只要看到两处重复决定同一事实,就需要回到单一来源原则。

调试不应从增加大量打印开始。先把启动页面 → 等待组件 → 语义定位 → 执行点击 → 检查反馈按顺序列出来,每次只在边界处记录不含个人信息的阶段与结果类别。这样即使没有真机性能指标,仍能通过构造输入与检查返回值定位哪一步发生分歧;反过来,如果只看最终页面颜色,就难以证明中间没有错误。

关于仅靠文案匹配容易受多语言影响,使用随机组件索引同样会使测试脆弱这一风险,建议把它写进实际测试用例的名称和断言,而不是只放在代码注释。注释会随着重构失效,测试中的输入与预期结果更容易暴露行为变化。测试本身同样要避免依赖随时变化的网络与时间,否则会把产品故障和测试环境偶然因素混在一起。

当方案需要扩展,优先问“新需求将改变哪个阶段”。如果它只改变执行点击,便不应触及后面的检查反馈;如果它改变最终业务语义,规则层和序列化层才需要一起调整。这个决策方式能够避免每加一个按钮就复制一份流程逻辑,也使连载中的代码可以逐篇成长。

这类问题的适用边界同样清楚:自动化用例应优先表达“找到某个可识别组件并执行行为”,而不是绑定一次截图的像素位置。当具体 SDK、设备能力或服务端协议发生变化,应重新查看对应的官方接口约束,再重新跑上述场景验证,而不是根据一张示例图推断系统行为。

官方资料

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐